:root {
    --void: #001a2d;
    --ink: #f7f9fb;
    --blood: #8b0000;
    --steel: #5f6e7c;
    --gold: #c9a634;
    --slate: #1a1a1a;
    --glow-blood: rgba(139,0,0,.55);
    --glow-gold: rgba(201,166,52,.55);
    --line: rgba(255,255,255,.06);
    /* A red bright enough to mark one value inside an already-red card,
       which --blood cannot do against itself. */
    --fault-edge: #ff4d4d;

    /* ── One card, one scale ───────────────────────────────────────────────
       Every listing in this application draws the same kind of object: a
       thing the archive holds, or one it is being asked about. These are the
       steps each part of one is set in, and every card reads them from here,
       so a title is the same size on the page after this one.

       The steps are the queue's, which is where they sit best.

         name     what the thing is called
         second   a second reading of it: an episode's own title, IMDb's
         fact     a year, a runtime, a rating, a cast, an id
         prose    a synopsis, a plot, what somebody typed
         mark     a state or an attribution, in micro-caps
         eyebrow  the smallest label, naming what a reading is
         figure   the gutter: a place in a queue, an episode number
         control  a card's own buttons */
    --card-name: 0.95rem;
    --card-second: 0.85rem;
    --card-fact: 0.78rem;
    --card-prose: 0.8rem;
    --card-prose-leading: 1.45;
    --card-prose-measure: 78ch;
    --card-mark: 0.66rem;
    --card-eyebrow: 0.62rem;
    --card-figure: 0.95rem;
    --card-control: 0.7rem;
    --card-control-pad: 0.25rem 0.6rem;

    /* ── One record, one step up ───────────────────────────────────────────
       A page showing one thing rather than a list of them (a report on its
       own, the film cued up to play) is read at arm's length rather than
       scanned, and is set a rung above the card scale. Every one of these
       pages moves by the same rung, so a heading is the same size on each of
       them and a step larger than the listing it came from.

       The roles are the card's, and a control is the same control wherever it
       is, so buttons stay on the card step. */
    --record-name: 1.35rem;
    --record-second: 1.05rem;
    --record-fact: 0.85rem;
    --record-prose: 0.9rem;
    --record-mark: 0.7rem;
    --record-eyebrow: 0.66rem;

    /* ── The ground a list entry sits on ───────────────────────────────────
       Slate, held a little short of solid, so the light and the grain the
       page is drawn on carry through a listing and the cards read as laid on
       it.

       Under the pointer a card closes up and warms towards the blood the rest
       of this application marks with, and its edge comes up in the same
       colour. These pages are read by running down forty rows of small type,
       and the row being read has to be findable without looking for it. */
    --card-ground: rgba(26, 26, 26, 0.82);
    --card-ground-lift: rgba(58, 16, 16, 0.97);
    --card-edge-lift: rgba(139, 0, 0, 0.7);
    /* The same two for a card flagged as needing attention, which trades the
       slate for the fault ground and has to carry white type on it. It is
       already the colour a card warms towards, so under the pointer it goes
       the other way: deeper, and a few degrees towards the void the page is
       drawn on. It stays plainly red: this is the state a maintainer scans a
       listing for. */
    --card-ground-fault: rgba(139, 0, 0, 0.85);
    --card-ground-fault-lift: rgba(99, 6, 16, 0.95);

    /* ── The ground the player speaks on ───────────────────────────────────
       The void gathered behind words laid over a picture, thinning to nothing
       well inside the frame. It carries white type over a bright scene without
       putting a panel on the shot, which is the whole reason the player says
       anything in the middle of the frame rather than in a box beside it.

       Held here rather than written into the plate, because the plate says four
       things: what is coming next, what went wrong, that the player stopped
       itself as it was asked to, and what the keys do, and they are one drawing
       carrying four contents. It is laid a little denser than the ground the
       paused state gathers, which carries a single word where these carry a
       title and a control. */
    --plate-ground: radial-gradient(ellipse 62% 56% at 50% 50%,
                                    rgba(0, 26, 45, 0.93) 0%,
                                    rgba(0, 26, 45, 0.72) 42%,
                                    rgba(0, 26, 45, 0) 76%);
    /* The same, drawn tighter, for the one reading that appears and goes. */
    --nudge-ground: radial-gradient(ellipse 34% 34% at 50% 50%,
                                    rgba(0, 26, 45, 0.9) 0%,
                                    rgba(0, 26, 45, 0) 78%);
}

html, body {
    height: 100%;
    margin: 0;
}

body {
    display: flex;
    flex-direction: column;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    /* One glow from above the masthead, as if the projector were behind it,
       over the gradient the page sinks through. */
    background:
        radial-gradient(ellipse 60% 60% at 50% -8%,
                        rgba(44, 104, 146, 0.6), rgba(29, 74, 107, 0) 75%),
        linear-gradient(180deg, #002a45 0%, var(--void) 40%, var(--void) 70%, #000d16 100%);
    /* Anchor the gradient to the viewport so it stays put while the page
       scrolls, matching the fixed screening room painted over it. Without
       this the gradient is sized to the full document and slides away on a
       long listing. */
    background-attachment: fixed;
    color: var(--ink);
}

/* The curtains of the screening room, hung down both sides of the window with
   the listings between them where the screen would be. Each is drawn from
   three runs of folds at widths that do not divide into one another, so they
   never settle into a pattern; lit from the glow above the masthead and
   sinking into the floor; and turned towards the light along its inner edge,
   where it casts a shadow onto the screen. All of it is soft gradient, so
   nothing fine ever lands behind type. */
.screening-room {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: -1;
}

.screening-room::before,
.screening-room::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: max(14px, calc(50% - 590px));
    --curtain-folds:
        linear-gradient(to bottom,
                        rgba(80, 140, 185, 0.18) 0%,
                        rgba(80, 140, 185, 0) 35%,
                        rgba(0, 6, 12, 0.62) 100%),
        repeating-linear-gradient(90deg,
                        rgba(0, 0, 0, 0.42) 0px,
                        rgba(0, 0, 0, 0) 15px,
                        rgba(110, 165, 210, 0.1) 24px,
                        rgba(0, 0, 0, 0) 33px,
                        rgba(0, 0, 0, 0.42) 47px),
        repeating-linear-gradient(90deg,
                        rgba(0, 0, 0, 0) 0px,
                        rgba(0, 0, 0, 0.3) 38px,
                        rgba(0, 0, 0, 0) 79px),
        repeating-linear-gradient(90deg,
                        rgba(110, 165, 210, 0) 0px,
                        rgba(110, 165, 210, 0.07) 13px,
                        rgba(110, 165, 210, 0) 29px),
        #042a45;
}

/* The inner edge turns towards the light: a narrow lit band where the
   fabric folds back, then the shadow the curtain throws onto the screen. */
.screening-room::before {
    left: 0;
    background:
        linear-gradient(to left, rgba(120, 175, 215, 0.16) 0px,
                        rgba(120, 175, 215, 0) 10px),
        var(--curtain-folds);
    box-shadow: 16px 0 40px rgba(0, 4, 9, 0.6);
}

.screening-room::after {
    right: 0;
    background:
        linear-gradient(to right, rgba(120, 175, 215, 0.16) 0px,
                        rgba(120, 175, 215, 0) 10px),
        var(--curtain-folds);
    box-shadow: -16px 0 40px rgba(0, 4, 9, 0.6);
}

/* ── Masthead ──────────────────────────────────────────────────────────────── */
/* The brand ribbon and the section rail beneath it are one instrument panel:
   the same void ground the library header sits on, divided by hairlines
   rather than by slabs, so the top of every page reads as a continuous strip
   down to the listing's own band. Gold appears in exactly two states here:
   where you are, and what is under the pointer, which is the same job it
   does in the readout and on the index rail. */
.navbar {
    background: var(--void);
    border-bottom: 1px solid var(--line);
    padding: 0.6rem 1.5rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.navbar-brand {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    text-decoration: none;
    color: var(--ink);
}

/* The mark, set to the cap height of the name beside it so the two read as one
   lockup rather than as an image with a caption. */
.navbar-brand-logo {
    display: block;
    width: 1.35rem;
    height: 1.35rem;
    flex: 0 0 auto;
}

.navbar-brand:hover,
.navbar-brand:focus {
    text-decoration: none;
}

.navbar-brand-text {
    display: flex;
    flex-direction: column;
}

/* Set in the same wide micro-caps as a page's own name, one step up in size
   and weight so the two read as the same voice at different volumes. */
/* The name is set in capitals here rather than written in them, so the
   configured value is the name as it would be written anywhere else. */
.navbar-brand-title {
    color: var(--ink);
    font-weight: 700;
    letter-spacing: 0.22em;
    font-size: 0.82rem;
    line-height: 1.4;
    text-transform: uppercase;
    transition: color 0.12s ease;
}

.navbar-brand:hover .navbar-brand-title {
    color: var(--gold);
}

/* What this installation is, under what it is called. Set a step down in size
   and weight and in the label colour, so it reads as the name's second line
   rather than as a second name: the same relationship a label has to a figure
   everywhere else here. */
.navbar-brand-subtitle {
    color: var(--steel);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-size: 0.6rem;
    font-weight: 600;
    line-height: 1.3;
}

/* The signed-in account. A username is an identifier a person recognises
   themselves by, so it is set in the face this application gives every
   identifier, and the control around it stays a hairline until it is reached
   for. */
.nav-user-btn {
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 3px;
    color: var(--steel);
    padding: 0.24rem 0.7rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.76rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    cursor: pointer;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    line-height: 1.5;
    transition: border-color 0.12s ease, color 0.12s ease;
}

.nav-user-btn:hover,
.nav-user-btn[aria-expanded="true"] {
    border-color: var(--gold);
    color: var(--gold);
    text-decoration: none;
}

/* ── Section rail ──────────────────────────────────────────────────────────── */
/* Where this system can be gone, as tabs: in two bars, because the six
   destinations answer two questions. Each is a plain label until it is the one
   you are on, and then it carries the gold underline this application already
   uses for a reading that is also somewhere to go, so "where you are" is
   marked the same way everywhere.

   Neither bar is named. What separates them is the ground each sits on: the
   library keeps the void the masthead above it is drawn on, and the bar under
   it takes the stop the page's own gradient closes at, so it reads as a shelf
   below rather than as a second rail. */
.admin-subnav {
    --bar-ground: var(--void);

    background: var(--bar-ground);
    border-bottom: 1px solid var(--line);
    padding: 0 1.5rem;
    display: flex;
    align-items: stretch;
    gap: 1.5rem;
    flex-wrap: wrap;
}

.admin-subnav-discover {
    --bar-ground: #000d16;
}

.admin-subnav-link {
    display: inline-flex;
    align-items: center;
    padding: 0.55rem 0;
    color: var(--steel);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    text-decoration: none;
    box-shadow: inset 0 -2px 0 transparent;
    transition: color 0.12s ease, box-shadow 0.12s ease;
}

.admin-subnav-link:hover {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 rgba(201, 166, 52, 0.45);
    text-decoration: none;
}

.admin-subnav-link.is-current {
    color: var(--gold);
    box-shadow: inset 0 -2px 0 var(--gold);
}

/* Narrow screens. Every destination stays on its bar and stays visible: what
   a phone lacks is width, and the answer to that is a bar set tighter, not a
   bar with half of it off the edge.

   The gap is what costs the most: 1.5rem between four tabs is more room than
   the labels themselves ask for, so it comes in first, and the type follows
   it down one step. A bar that still runs past the edge wraps the way it
   already knows how to, under the name it belongs to. */
@media (max-width: 767px) {
    .navbar {
        padding: 0.55rem 0.9rem;
        gap: 0.6rem;
    }

    /* Tracking is what makes a name wide, and width is the one thing in short
       supply here. Loosened enough to stay the same voice, tight enough to
       leave the account beside it somewhere to sit. */
    .navbar-brand-title {
        letter-spacing: 0.16em;
    }

    .admin-subnav {
        padding: 0 0.9rem;
        gap: 0.1rem 1rem;
    }

    .admin-subnav-link {
        padding: 0.5rem 0;
        font-size: 0.66rem;
        letter-spacing: 0.1em;
    }
}

/* The sign-in bar on a public preview, standing in the slot the section bars
   fill for a signed-in reader. Red, because it is the one thing on the page
   asking to be acted on, and it carries the voice the section bars are set
   in. The whole bar is the link. */
.preview-signin-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.55rem 1.5rem;
    background: var(--blood);
    border-bottom: 1px solid rgba(0, 0, 0, 0.35);
    color: #f3d3d3;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    text-decoration: none;
    transition: background 0.12s ease, color 0.12s ease;
}

.preview-signin-bar:hover,
.preview-signin-bar:focus-visible {
    background: #a10a0a;
    color: #fff;
    text-decoration: none;
}

.preview-signin-cta {
    display: inline-flex;
    align-items: center;
    color: #fff;
    white-space: nowrap;
}

@media (max-width: 767px) {
    .preview-signin-bar {
        padding: 0.5rem 0.9rem;
        gap: 0.75rem;
        font-size: 0.66rem;
        letter-spacing: 0.1em;
    }
}

/* Dropdown */
.dropdown-menu {
    background: var(--slate) !important;
    border: 1px solid var(--line) !important;
}

.dropdown-item {
    color: var(--ink) !important;
}

.dropdown-item:hover,
.dropdown-item:focus {
    background: var(--void) !important;
    color: var(--gold) !important;
}

.dropdown-divider {
    border-color: var(--line) !important;
}

/* Main content */
.main-content {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
}

.main-content.dashboard-content {
    align-items: flex-start;
    padding-top: 2rem;
}

/* Login card */
.login-card {
    background: var(--slate);
    border: 1px solid var(--line);
    border-radius: 4px;
    width: 100%;
    max-width: 420px;
    box-shadow: 0 8px 30px rgba(0,0,0,0.4);
    color: var(--ink);
}

.login-header {
    text-align: center;
    padding: 2rem 2rem 1rem;
    border-bottom: 1px solid var(--line);
}

.login-header h1 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--ink);
    letter-spacing: 0.1em;
    margin: 0.5rem 0 0;
}

.login-header .subtitle {
    display: block;
    color: var(--steel);
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.login-failed {
    max-width: 420px;
    background: var(--blood);
    border-left: 3px solid #ff4444;
    color: var(--ink);
}

.login-actions {
    display: flex;
    gap: 0.5rem;
}

/* Forms */
.form-group {
    margin-bottom: 1rem;
}

label, .form-label {
    display: block;
    font-size: 0.8rem;
    color: var(--steel);
    margin-bottom: 0.35rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 600;
}

.form-control {
    display: block;
    width: 100%;
    padding: 0.6rem 0.75rem;
    font-size: 0.95rem;
    font-family: inherit;
    color: var(--ink) !important;
    background: var(--void) !important;
    border: 1px solid var(--steel) !important;
    border-radius: 3px;
    outline: none;
    box-sizing: border-box;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.form-control:focus {
    border-color: var(--gold) !important;
    box-shadow: 0 0 0 2px var(--glow-gold) !important;
}

.form-control::placeholder {
    color: var(--steel) !important;
    opacity: 0.6;
}

.form-control:disabled,
.form-control[readonly] {
    background: rgba(0,26,45,0.5) !important;
    color: var(--steel) !important;
    border-color: rgba(95,110,124,0.3) !important;
}

select.form-control {
    appearance: auto;
}

/* Buttons */
.btn-primary {
    display: block;
    width: 100%;
    padding: 0.7rem 1.5rem;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--void) !important;
    background: var(--gold) !important;
    border: none !important;
    border-radius: 3px;
    cursor: pointer;
    transition: background 0.15s ease;
}

.btn-primary:hover,
.btn-primary:focus {
    background: #b8952e !important;
    color: var(--void) !important;
}

.btn-gold {
    background: var(--gold) !important;
    color: var(--void) !important;
    border: none !important;
    padding: 0.5rem 1.5rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-size: 0.9rem;
    border-radius: 3px;
    transition: background 0.15s ease;
}

.btn-gold:hover {
    background: #b8952e !important;
    color: var(--void) !important;
}

.btn-void {
    background: var(--void) !important;
    color: var(--ink) !important;
    border: 1px solid var(--steel) !important;
    padding: 0.5rem 1.5rem;
    font-weight: 600;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-radius: 3px;
    transition: border-color 0.15s ease;
}

.btn-void:hover {
    border-color: var(--gold) !important;
    color: var(--gold) !important;
}

.btn-blood {
    background: var(--blood) !important;
    color: var(--ink) !important;
    border: none !important;
    padding: 0.5rem 1rem;
    font-weight: 600;
    border-radius: 3px;
}

.btn-blood:hover {
    background: #a00000 !important;
}

.btn-outline-secondary {
    color: var(--steel) !important;
    border-color: var(--steel) !important;
    background: transparent !important;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.btn-outline-secondary:hover {
    color: var(--ink) !important;
    border-color: var(--ink) !important;
    background: rgba(255,255,255,0.05) !important;
}

/* Panel cards */
.panel-card {
    background: var(--slate);
    border: 1px solid var(--line);
    border-radius: 4px;
    box-shadow: 0 8px 30px rgba(0,0,0,0.4);
    color: var(--ink);
    margin-bottom: 1.5rem;
    overflow: hidden;
}

.panel-card-body {
    padding: 1.5rem;
}

/* Links */
a {
    color: var(--gold);
    transition: color 0.1s ease;
}

a:hover {
    color: #b8952e;
}

/* Alerts */
.alert-danger,
.alert-danger.alert-dismissible {
    background: var(--blood) !important;
    border-color: var(--blood) !important;
    color: var(--ink) !important;
    border-radius: 3px;
}

.alert-danger .close,
.alert-danger .close:hover {
    color: var(--ink) !important;
    opacity: 0.8;
    text-shadow: none;
}

.alert-error {
    background: var(--blood);
    border-left: 3px solid #ff4444;
    color: var(--ink);
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    border-radius: 3px;
}

.alert-success,
.alert-success.alert-dismissible {
    background: rgba(26,122,26,0.4) !important;
    border-color: #1a7a1a !important;
    color: var(--ink) !important;
    border-radius: 3px;
}

.alert-success .close,
.alert-success .close:hover {
    color: var(--ink) !important;
    opacity: 0.8;
    text-shadow: none;
}

/* Toast */
.site-toast {
    position: fixed;
    top: 1rem;
    right: 1rem;
    padding: 0.75rem 1.5rem;
    border-radius: 4px;
    border-left: 3px solid;
    color: var(--ink);
    font-weight: 600;
    font-size: 0.9rem;
    z-index: 9999;
    opacity: 0;
    transition: opacity 0.3s ease;
    box-shadow: 0 4px 15px rgba(0,0,0,0.4);
}

/* Badges */
.badge {
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 0.7rem;
}

/* Headings */
h1, h2, h3, h4, h5, h6 {
    color: var(--ink);
}

/* Utility */
.color-steel { color: var(--steel); }
.color-ink { color: var(--ink); }
.code-steel { color: var(--steel); font-size: 0.75rem; }

/* ── Footer ────────────────────────────────────────────────────────────────── */
/* The page closes on the band it opens with. The masthead, the section rail and
   this are the same strip of instrument panel: void ground, divided by the
   hairline everything in this application is divided by, on the same 1.5rem
   margin, so the page is bounded top and bottom by one rule rather than
   opening on a hairline and closing on a slab.
   What it carries is a meta line about the session, and this application
   already draws meta lines: a mark in micro-caps, then the reading in the
   figure face. It is the line a conversion row closes with, a report row
   closes with, and a request row closes with, set at the scale of the page
   instead of the scale of a card. */
.footer {
    background: var(--void);
    border-top: 1px solid var(--line);
    padding: 0.7rem 1.5rem;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.35rem 1.5rem;
    flex-shrink: 0;
}

/* One size across the whole strip. The band is twenty pixels tall and holds
   three kinds of thing; set at three sizes it reads as ragged however the
   baselines actually sit, so what tells them apart is the face and the colour
   rather than the scale. */
.footer,
.footer .copyright,
.footer-meta-label,
.footer-meta-value {
    font-size: 0.66rem;
}

/* Who holds this installation. A statement rather than a reading. */
.footer .copyright {
    letter-spacing: 0.08em;
    color: var(--steel);
}

.footer-meta {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.2rem 1.2rem;
}

.footer-meta-item {
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    white-space: nowrap;
}

/* Names the reading beside it, in the micro-caps this application names every
   reading with. Tracked a step tighter than a card's label, because two words
   held at a card's tracking read as stretched rather than as small caps. */
.footer-meta-label {
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--steel);
}

/* An address and a time. Both are read character by character, so both take
   the figure face, which is the whole of what marks them out. They stay in
   the label colour, lifted only enough to separate them from the word in front
   of them: this band is the quietest on the page, and the brightest ink in
   this application belongs to what somebody is looking for rather than to the
   address they happen to be at. */
.footer-meta-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    color: rgba(247, 249, 251, 0.55);
}

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

.footer a:hover,
.footer a:focus {
    color: var(--gold);
}

.footer a:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

@media (max-width: 576px) {
    .login-card { margin: 0 0.5rem; }
    .navbar-brand-subtitle { display: none; }
    .footer {
        flex-direction: column;
        align-items: center;
        text-align: center;
        padding: 0.6rem 1rem;
    }
    .login-actions {
        flex-direction: column;
    }
    .login-actions .btn { width: 100%; }
}

/* ── Library header ────────────────────────────────────────────────────────── */
/* The first half of one continuous band. It shares the ground and the bleed
   of the controls directly beneath it and carries no rule of its own, so the
   two read as a single strip: this half scrolls away, the half below it
   pins. Given a rule and a margin instead, it floats between the subnav
   above and the controls below and belongs to neither. */
.library-header {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.35rem 1.1rem;
    background: var(--void);
    margin: 0 -1rem;
    padding: 0.85rem 1rem 0.45rem;
}

/* The subnav has already said which listing this is, so the page name is set
   as a quiet marker rather than a headline: present for the document's
   structure and for orientation, not repeating itself at display size. */
.library-name {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink);
    margin: 0;
    white-space: nowrap;
}

/* The part of a page's name that is also the way back up from it: a season
   naming its show. Set as the heading it sits in and marked only when it is
   reached for, so the name reads as a name rather than as a control. */
.library-name-link {
    color: inherit;
    text-decoration: none;
}

.library-name-link:hover,
.library-name-link:focus {
    color: var(--gold);
    text-decoration: none;
    box-shadow: inset 0 -2px 0 var(--gold);
}

/* The state of the listing as one line. Figure first because the figure is
   what is read; the label follows it the way a unit follows a measurement. */
.library-readout {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.2rem 1.1rem;
    min-width: 0;
}

.library-stat {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3rem;
    text-decoration: none;
    white-space: nowrap;
}

.library-stat-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 0.95rem;
    font-weight: 600;
    line-height: 1;
    color: var(--ink);
}

.library-stat-label {
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--steel);
}

/* Nothing wrong is good news here, so a zero steps back and lets the counts
   that are asking for something hold the line. */
.library-stat.is-zero .library-stat-value {
    color: var(--steel);
    font-weight: 400;
}

/* The one reading that is also somewhere to go. */
a.library-stat-action .library-stat-value {
    color: var(--gold);
    box-shadow: inset 0 -2px 0 rgba(201, 166, 52, 0.35);
}

a.library-stat-action:hover .library-stat-value {
    box-shadow: inset 0 -2px 0 var(--gold);
}

a.library-stat-action:hover .library-stat-label {
    color: var(--gold);
}

/* The concern being viewed. Marked in the accent like the links beside it,
   but without their underline, because it is not somewhere to go. */
.library-stat.is-current .library-stat-value,
.library-stat.is-current .library-stat-label {
    color: var(--gold);
}

/* What the listing holds and how to get around it, said once under the
   readout. It continues the header's band: same ground, same bleed, no rule
   between them, and is set in the band's own quiet register so it reads as a
   caption to the readings above it rather than as the page's first paragraph.
   The measure is short enough that the sentence is read in one pass. */
.library-note {
    background: var(--void);
    margin: 0 -1rem;
    /* The same inset the readout above it and the search below it take, so the
       three start and stop on one line down both edges of the band. */
    padding: 0.4rem 1rem 0.75rem;
    font-size: 0.85rem;
    line-height: 1.55;
    color: var(--steel);
}

/* A description long enough to be said in several paragraphs keeps them apart
   by the space between them, on the band's ground and at the band's inset. */
.library-note p {
    margin: 0 0 0.55rem;
}

.library-note p:last-child {
    margin-bottom: 0;
}

/* A page with no control strip under it closes the band itself. */
.library-header-standalone {
    padding-bottom: 0.85rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--line);
}

.library-header-action {
    margin-left: auto;
}

/* ── Search and letter index ───────────────────────────────────────────────── */
/* Pinned to the top of the viewport while the listing scrolls: the control
   that moves you to another letter should never itself require scrolling to
   reach. It carries its own opaque ground because the page background is a
   fixed gradient that cards would otherwise show through as they pass under. */
.library-controls {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--void);
    margin: 0 -1rem 1rem;
    padding: 0.55rem 1rem 0;
    border-bottom: 1px solid var(--line);
    box-shadow: 0 6px 14px -8px rgba(0, 0, 0, 0.85);
}

/* The search sits on the page rather than in a card, on the same hairline
   language as the header above it, so the cards below stay the only boxes. */
.library-search {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.75rem;
}

.library-search-field {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
}

/* Room at the right edge of the field for the clearing control that sits
   inside it. */
.library-search-field .form-control {
    width: 100%;
    padding-right: 2.1rem;
}

/* Clearing a search happens in the field the search was typed into, so the
   icon rides inside the input and appears only once there is a term to drop.
   The browser's own cancel button is turned off to leave one control. */
.library-search-clear {
    position: absolute;
    top: 50%;
    right: 0.6rem;
    transform: translateY(-50%);
    line-height: 1;
    font-size: 0.78rem;
    color: var(--steel);
    transition: color 0.12s ease;
}

.library-search-clear:hover,
.library-search-clear:focus {
    color: var(--gold);
}

.library-search .form-control::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

.library-search .btn {
    flex: 0 0 auto;
}

/* Which order the listing is read in. A short line of words above the letter
   rail, in the micro-caps this application names a reading with: the order
   being read is marked the way a current reading is marked everywhere here,
   and the others are plain until they are reached for. */
.library-sort {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.2rem 0.9rem;
    padding-bottom: 0.6rem;
}

.library-sort-label {
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
}

.library-sort-option {
    display: inline-flex;
    align-items: baseline;
    gap: 0.35rem;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
    text-decoration: none;
    white-space: nowrap;
}

a.library-sort-option:hover,
a.library-sort-option:focus {
    color: var(--ink);
    text-decoration: none;
}

.library-sort-option.is-current {
    color: var(--gold);
    box-shadow: inset 0 -2px 0 var(--gold);
}

a.library-sort-option:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* The arrow the year control turns over. Set below the word it follows,
   because it is a direction rather than part of the name. */
.library-sort-option .fas {
    font-size: 0.58rem;
}

/* The index rail. Letters that lead nowhere in the current listing are still
   shown, so the alphabet keeps its shape and a gap reads as information
   (nothing is filed there) rather than as a missing control. */
.alpha-seek {
    display: flex;
    flex-wrap: wrap;
    gap: 0.1rem;
    padding-bottom: 0.7rem;
}

.alpha-seek-letter {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1;
    padding: 0.3rem 0.42rem;
    border-radius: 2px;
    text-decoration: none;
    color: var(--gold);
}

a.alpha-seek-letter:hover,
a.alpha-seek-letter:focus-visible {
    background: var(--gold);
    color: var(--void);
    text-decoration: none;
}

/* The letter currently being shown. Filled rather than tinted, because it is
   the one piece of state the listing depends on. */
a.alpha-seek-letter.is-active {
    background: var(--gold);
    color: var(--void);
}

.alpha-seek-letter.is-empty {
    color: var(--steel);
    opacity: 0.4;
}

/* Every initial at once, leading the rail. It is not one of the alphabet: it
   is the whole listing, which is a different kind of thing from a place in it,
   so it is not dressed as a letter: set in the micro-caps this application
   names a reading with rather than in the figure face the letters take, boxed
   in the hairline it draws a control with, and given room before the alphabet
   begins.

   What it keeps is the fill, because being the reading in force is marked the
   same way wherever it happens. */
.alpha-seek-all {
    font-family: inherit;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    padding: 0.28rem 0.5rem;
    margin-right: 0.5rem;
    border: 1px solid var(--line);
    color: var(--steel);
}

a.alpha-seek-all:hover,
a.alpha-seek-all:focus-visible {
    background: transparent;
    border-color: var(--gold);
    color: var(--gold);
}

.alpha-seek-all.is-active {
    background: var(--gold);
    border-color: var(--gold);
    color: var(--void);
}

/* ── The genre tags ────────────────────────────────────────────────────────── */
/* Every genre the listing holds, as the tags each of them is. Folded away by
   default: it is thirty words standing above the listing they are there to
   narrow, and it opens itself the moment anything is chosen, so what is
   filtering the page is never behind a control.

   A disclosure rather than a scripted panel, which is what the biography on a
   filmography already uses: the element the browser knows answers the keyboard,
   is found by a page search, and works with nothing running. */
.library-genres {
    padding-bottom: 0.6rem;
}

/* The genres and the interests, side by side: two filters of one kind, each
   taking half the strip, so opening them costs one band of the pinned controls
   rather than two stacked on top of each other. On a narrow screen there is no
   half worth having, and they stack. */
.library-tag-filters {
    display: flex;
    align-items: flex-start;
    gap: 0 2rem;
}

.library-tag-filters > .library-genres {
    flex: 1 1 0;
    min-width: 0;
}

@media (max-width: 767px) {
    .library-tag-filters {
        flex-direction: column;
    }

    .library-tag-filters > .library-genres {
        flex: 0 0 auto;
        width: 100%;
    }
}

.library-genres > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.2rem 0.7rem;
}

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

/* Named in the micro-caps this application names every reading with, so the
   panel reads as a label on the listing rather than as a heading over it. */
.library-genres-label {
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
}

.library-genres > summary:hover .library-genres-label,
.library-genres > summary:focus-visible .library-genres-label {
    color: var(--ink);
}

/* What is narrowing the page, carried on the control that opens the tags:
   folded away, this is the only place the filter is said. In the accent,
   because it is a reading in force. */
.library-genres-chosen {
    font-size: var(--card-fact);
    font-weight: 600;
    color: var(--gold);
    min-width: 0;
    word-break: break-word;
}

.library-genres > summary .fas {
    font-size: 0.58rem;
    color: var(--steel);
    transition: transform 0.12s ease;
}

.library-genres[open] > summary .fas {
    transform: rotate(180deg);
}

.library-genres > summary:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* The tags themselves, read as one comma-separated line of type rather than as
   a rank of buttons: there are thirty of them, and thirty filled shapes above
   a listing would be read before the listing is. Capped in height so a library
   with a long vocabulary cannot push the listing off the screen, since the
   whole strip is pinned while the page scrolls. */
.library-genres-tags {
    margin-top: 0.5rem;
    max-height: 8.5rem;
    overflow-y: auto;
    font-size: var(--card-fact);
    line-height: 1.9;
    color: var(--steel);
}

/* One tag. Plain until it is reached for, which is how every name in this
   application that is also a control is set. */
.library-genres-tag {
    color: var(--steel);
    text-decoration: none;
    white-space: nowrap;
    padding: 0.1rem 0.3rem;
    border-radius: 2px;
}

.library-genres-tag:hover,
.library-genres-tag:focus-visible {
    color: var(--gold);
    text-decoration: none;
}

/* A tag in force. Filled rather than tinted, the way the letter being shown is
   filled on the index rail: these two are the state the listing depends on,
   and they are marked the same. */
.library-genres-tag.is-on {
    background: var(--gold);
    color: var(--void);
}

/* Held to the anchors, because the tag in force is a link that takes itself off
   again while All in force is a state with nowhere to go, and a thing that
   answers the pointer without being pressable is a thing that lies about
   itself. */
a.library-genres-tag.is-on:hover,
a.library-genres-tag.is-on:focus-visible {
    background: #b8952e;
    color: var(--void);
}

.library-genres-tag:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 1px;
}

/* The comma belongs to the line rather than to the tag, so it is not part of
   what gets clicked and does not take the filled ground beside it. */
.library-genres-comma {
    color: var(--steel);
}

/* Every genre at once, leading the line: the listing with none of them
   narrowing it, and the one control that takes them all off.

   It is not one of the genres, so it is not dressed as one: no hash, the
   micro-caps this application names a reading with rather than the body face
   the tags are read in, boxed in the hairline it draws a control with, and
   standing off the line rather than sitting in it behind a comma. It is the
   same thing All is at the head of the index rail and is marked the same way,
   so the two read as one control answering for two filters. */
.library-genres-all {
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    padding: 0.16rem 0.5rem;
    margin-right: 0.35rem;
    border: 1px solid var(--line);
    color: var(--steel);
}

a.library-genres-all:hover,
a.library-genres-all:focus-visible {
    border-color: var(--gold);
    color: var(--gold);
}

.library-genres-all.is-on {
    background: var(--gold);
    border-color: var(--gold);
    color: var(--void);
}

@media (prefers-reduced-motion: reduce) {
    .library-genres > summary .fas {
        transition: none;
    }
}

/* The genres on a card, each the tag the listing above filters by, so what is
   read on a card is what is clicked over it. Set in the line a series carries
   the rest of its shape on, and kept to that line's own colour until reached
   for: a card is read for the title, and a row of accents across every one of
   them would be read first. */
.library-card-genres {
    gap: 0.15rem 0.55rem;
}

.library-card-genre {
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

.library-card-genre:hover,
.library-card-genre:focus {
    color: var(--gold);
    text-decoration: underline;
}

.library-card-issue .library-card-genre:hover,
.library-card-issue .library-card-genre:focus {
    color: var(--ink);
}

.library-card-genre:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

.navbar-brand:focus-visible,
.nav-user-btn:focus-visible,
.admin-subnav-link:focus-visible,
a.library-stat-action:focus-visible,
a.alpha-seek-letter:focus-visible,
.library-search .form-control:focus-visible,
.library-search-clear:focus-visible,
.library-card-controls .btn:focus-visible,
.queue-row-controls .btn:focus-visible,
a.queue-row-title:focus-visible,
.episode-row-queue:focus-visible,
.library-card-poster-zoom:focus-visible,
a.library-reconcile-title:focus-visible,
.library-card-billing-name a:focus-visible,
.library-card-cast-name a:focus-visible,
.library-name-link:focus-visible,
.season-card-open:focus-visible,
button.season-card-directory:focus-visible,
button.season-card-number:focus-visible,
button.episode-row-name:focus-visible,
button.episode-row-number:focus-visible,
.library-reconcile-id a:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

.library-card-issue .library-card-controls .btn:focus-visible,
.library-card-issue .library-reconcile-id a:focus-visible {
    outline-color: var(--ink);
}

/* Nothing here is load-bearing motion: the transitions are hover feedback
   only, so they come off entirely when the viewer has asked for that. */
@media (prefers-reduced-motion: reduce) {
    .library-card,
    .episode-row,
    .library-stat,
    a.alpha-seek-letter,
    .navbar-brand-title,
    .nav-user-btn,
    .admin-subnav-link {
        transition: none;
    }
}

/* ── Library cards ─────────────────────────────────────────────────────────── */
/* One card per entry, stacked one per row, in both the movies and the shows
   listing. Each card is a reconciliation: the archive's reading of a title on
   the first line, IMDb's on the second, sharing one grid so title and year
   align column for column. A card that disagrees with itself shows it as a
   broken column before it shows it as words, which is the only thing these
   listings exist to help someone do.

   Tighter than a stock panel-card, because the listing runs to thousands of
   rows and the vertical rhythm has to hold across all of them. */
.library-card {
    background: var(--card-ground);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.6rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.library-card:hover {
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
    background: var(--card-ground-lift);
}

.library-card-body {
    display: flex;
    align-items: flex-start;
    gap: 1.1rem;
    padding: 0.9rem 1.1rem;
}

/* Poster column. The image is sized rather than the column, so the column
   collapses to whatever the artwork needs and the detail column takes the
   rest. The hairline frame reads as a mounted still rather than a raw image
   dropped into a list. */
.library-card-poster {
    flex: 0 0 auto;
}

.library-card-poster-image,
.library-card-poster-empty {
    width: 4.25rem;
    border-radius: 2px;
    display: block;
    box-shadow: 0 0 0 1px rgba(201, 166, 52, 0.25), 0 2px 8px rgba(0, 0, 0, 0.45);
}

.library-card-poster-image {
    height: auto;
    background: var(--void);
}

/* Stand-in when there is no artwork. Proportioned to a poster so a card
   without one keeps the same shape as its neighbours. */
.library-card-poster-empty {
    aspect-ratio: 2 / 3;
    background: var(--void);
    color: var(--steel);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.2rem;
    box-shadow: inset 0 0 0 1px var(--line);
}

.library-card-detail {
    flex: 1 1 auto;
    min-width: 0;
}

/* Four columns shared by both readings: the side label, the title, the year,
   and IMDb's id. Years and ids are set in the monospace face so the rows
   align to the character and a mismatch registers as a jump. */
.library-reconcile {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr) max-content max-content;
    align-items: baseline;
    column-gap: 0.9rem;
    row-gap: 0.15rem;
}

/* Switching a dialog between its two answers, set as a line of type at the far
   end of the footer so it does not compete with the control that carries the
   dialog out. */
.move-into-show-mode {
    margin-right: auto;
    padding: 0;
    border: 0;
    background: none;
    color: var(--gold);
    font: inherit;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    cursor: pointer;
}

.move-into-show-mode:hover {
    text-decoration: underline;
}

.move-into-show-mode:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* ── Taking the name from IMDb ─────────────────────────────────────────────── */
/* A control inside the rename dialog, set as a line of type rather than as a
   second button competing with the one that carries the rename out. What it
   would fill in is named beside it, so nobody has to press it to find out. */
.edit-entry-adopt {
    margin-top: 0.9rem;
    padding: 0;
    border: 0;
    background: none;
    color: var(--gold);
    font: inherit;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    cursor: pointer;
}

.edit-entry-adopt:hover:not(:disabled) {
    text-decoration: underline;
}

.edit-entry-adopt:disabled {
    color: var(--steel);
    cursor: default;
}

.edit-entry-adopt:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* What the control would put in the fields. */
.edit-entry-adopt-note {
    display: block;
    margin-top: 0.25rem;
    font-size: 0.78rem;
    color: var(--steel);
}

.edit-entry-adopt-note:empty {
    display: none;
}

/* IMDb's title is also the way to IMDb's page for it. It stays the reading it
   is (same size, same accent) and is marked only when it is reached for, so
   the row reads as two readings rather than as a control. */
a.library-reconcile-title {
    text-decoration: none;
}

a.library-reconcile-title:hover,
a.library-reconcile-title:focus {
    color: var(--gold);
    text-decoration: none;
    box-shadow: inset 0 -2px 0 var(--gold);
}

.library-card-issue a.library-reconcile-title:hover,
.library-card-issue a.library-reconcile-title:focus {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 var(--ink);
}

/* Names which reading a row is. Kept quiet: it should never compete with
   the reading itself. */
.library-reconcile-side {
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
    padding-top: 0.35rem;
}

/* The archive's title is the card's headline as well as the top half of the
   comparison, so it carries the weight for both jobs. */
.library-card-title {
    font-size: var(--card-name);
    font-weight: 600;
    line-height: 1.4;
    color: var(--ink);
    margin: 0;
    min-width: 0;
    word-break: break-word;
}

/* IMDb's reading of the same film, set as the second reading it is and in the
   accent, so the two sources stay distinguishable while scanning a long
   listing. */
.library-reconcile-title {
    font-size: var(--card-second);
    color: var(--gold);
    min-width: 0;
    word-break: break-word;
}

.library-reconcile-year,
.library-reconcile-id {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Both years are set at one step, so the column they share lands on one
   measure and a disagreement between them registers as a jump. Which reading a
   year belongs to is carried by its weight and its colour. */
.library-card-title + .library-reconcile-year {
    font-size: var(--card-fact);
    font-weight: 600;
    color: var(--ink);
}

/* IMDb's year follows IMDb's title, and takes the accent with it. */
.library-reconcile-title + .library-reconcile-year {
    font-size: var(--card-fact);
    font-weight: 400;
    color: var(--gold);
}

.library-reconcile-id {
    font-size: var(--card-fact);
    letter-spacing: 0.02em;
}

.library-reconcile-id a {
    color: var(--steel);
}

.library-reconcile-id a:hover {
    color: var(--gold);
}

/* A value neither side has. Dimmed rather than marked: an absence is not
   the same thing as a disagreement. */
.library-reconcile .is-absent {
    color: var(--steel);
    font-weight: 400;
}

/* A value contradicting the row above it. This is the mark the whole layout
   exists to deliver, so it is the one place colour is spent inside an
   otherwise quiet card. */
.library-reconcile .is-mismatch {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 var(--fault-edge);
}

/* The shape of a series: how many seasons and episodes, how long it ran, how
   it is rated and filed. Read as one line of facts rather than as a set of
   fields, so the parts are separated by spacing alone and the seasons figure,
   which is the one being checked against a directory of season folders, is
   the only part set in the face this application gives its figures. */
.library-card-shape {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.9rem;
    margin-top: 0.5rem;
    font-size: var(--card-fact);
    color: var(--steel);
}

.library-card-shape-figure {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}

/* The seasons the archive does not hold, marked inside the line rather than
   set apart from it: the reader is already looking at the season figure, and
   what is absent belongs beside what is present. */
.library-card-shape-fault {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    color: var(--fault-edge);
}

.library-card-issue .library-card-shape {
    color: rgba(255, 255, 255, 0.72);
}

.library-card-issue .library-card-shape-fault {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 var(--ink);
}

.library-card-issue .library-card-shape-figure {
    color: var(--ink);
}

/* Square hairline labels rather than filled pills: several appear at once on
   a bad card, and filled shapes at that density read as noise. */
.library-card-badges {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-top: 0.55rem;
}

.library-card-badges:empty {
    display: none;
}

.library-flag {
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    padding: 0.15rem 0.45rem;
    border: 1px solid currentColor;
    border-radius: 2px;
    white-space: nowrap;
}

.library-flag-fault {
    color: var(--fault-edge);
}

.library-flag-warn {
    color: var(--gold);
}

.library-flag-gone {
    color: var(--steel);
}

/* Who directed the film, on the line above the people in it. Set as a labelled
   fact rather than as another list of names: the label is what tells this name
   from the cast beneath it, and the name itself takes the ink the archive's own
   readings are set in, since it answers a question about the film rather than
   about what else is in the library. */
.library-card-director {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.1rem 0.4rem;
    margin: 0.6rem 0 0;
    font-size: var(--card-fact);
    color: var(--steel);
}

.library-card-director-label {
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.library-card-director-name {
    color: var(--ink);
    white-space: nowrap;
}

/* Two directors are read as a pair rather than as a column of one-word rows, so
   they are separated the way a cast is. */
.library-card-director-name:not(:last-child)::after {
    content: ',';
    color: var(--steel);
}

/* The name is a control, the way a performer's is: plain until it is reached
   for, then the accent. */
.library-card-director-name a {
    color: inherit;
    text-decoration: none;
}

.library-card-director-name a:hover,
.library-card-director-name a:focus {
    color: var(--gold);
    text-decoration: underline;
}

.library-card-issue .library-card-director-name a:hover,
.library-card-issue .library-card-director-name a:focus {
    color: var(--ink);
}

/* On a flagged card the palette is forced to white, so the label keeps its
   quieter register by weight rather than by colour. */
.library-card-issue .library-card-director {
    color: rgba(255, 255, 255, 0.72);
}

.library-card-issue .library-card-director-name {
    color: var(--ink);
}

/* The performers IMDb ships with the title. Kept in its own quieter register
   above the collected billing, because the two are different depths of the
   same answer and a card should still show which came with the link. Every
   name is a search for that performer within this listing. */
.library-card-cast {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.1rem 0.3rem;
    list-style: none;
    margin: 0.6rem 0 0;
    padding: 0;
    font-size: var(--card-fact);
    color: var(--steel);
}

.library-card-cast-name {
    white-space: nowrap;
}

.library-card-cast-name:not(:last-child)::after {
    content: ',';
}

.library-card-cast-name a {
    color: inherit;
    text-decoration: none;
}

.library-card-cast-name a:hover,
.library-card-cast-name a:focus {
    color: var(--gold);
    text-decoration: underline;
}

.library-card-issue .library-card-cast-name a:hover,
.library-card-issue .library-card-cast-name a:focus {
    color: var(--ink);
}

/* The collected billing, running on beneath the names IMDb ships with the
   title. Set in the accent the IMDb row above it uses, because it is the same
   source answering about the same title, and read as a cast list, comma to
   comma, rather than as a rank of separate items. Every name is a search for
   that performer within this listing. */
.library-card-billing {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.1rem 0.3rem;
    list-style: none;
    margin: 0.35rem 0 0;
    padding: 0;
}

.library-card-billing-name {
    font-size: var(--card-fact);
    white-space: nowrap;
}

.library-card-billing-name:not(:last-child)::after {
    content: ',';
    color: var(--steel);
}

.library-card-billing-name a {
    color: var(--gold);
    text-decoration: none;
}

.library-card-billing-name a:hover,
.library-card-billing-name a:focus {
    color: var(--gold);
    text-decoration: underline;
}

/* On a flagged card the palette is forced to white, so the accent that
   distinguishes these names elsewhere would disappear into the red. */
.library-card-issue .library-card-billing-name a {
    color: var(--ink);
}

.library-card-issue .library-card-billing-name:not(:last-child)::after {
    color: rgba(255, 255, 255, 0.72);
}

/* The synopsis, closing the card. Set looser than the lines above it, which
   are compared, and held to a comfortable measure so it does not run the full
   width of a wide screen. */
.library-card-description {
    margin: 0.6rem 0 0;
    padding-top: 0.6rem;
    border-top: 1px solid var(--line);
    max-width: var(--card-prose-measure);
    font-size: var(--card-prose);
    line-height: var(--card-prose-leading);
    color: var(--ink);
}

/* ── Movie card: needs attention ───────────────────────────────────────────── */
/* A card whose entry has any coherency problem: a title or year that
   disagrees with IMDb, a link that resolves to nothing, a missing link, a
   misnamed directory, a directory that is gone, turns red so the listing
   can be scanned for trouble rather than read. Everything inside it is
   forced to white, because the normal palette puts gold and steel on a
   dark ground and neither survives being moved onto red. */
.library-card-issue {
    background: var(--card-ground-fault);
    border-color: var(--fault-edge);
    border-left-color: var(--fault-edge);
    color: var(--ink);
}

/* The frame stays the fault's own, so a card that has settled towards the void
   under the pointer is still plainly one with something wrong with it. */
.library-card-issue:hover {
    background: var(--card-ground-fault-lift);
    border-color: var(--fault-edge);
    border-left-color: var(--ink);
}

.library-card-issue .library-card-title,
.library-card-issue .library-reconcile-title,
.library-card-issue .library-reconcile-year,
.library-card-issue .library-card-title + .library-reconcile-year,
.library-card-issue .library-reconcile-title + .library-reconcile-year,
.library-card-issue .library-card-description,
.library-card-issue .color-steel {
    color: var(--ink);
}

.library-card-issue .library-reconcile-side,
.library-card-issue .library-card-cast,
.library-card-issue .library-reconcile .is-absent,
.library-card-issue .library-reconcile-id a {
    color: rgba(255, 255, 255, 0.72);
}

.library-card-issue .library-reconcile-id a:hover {
    color: var(--ink);
}

.library-card-issue .library-reconcile .is-mismatch {
    box-shadow: inset 0 -2px 0 var(--ink);
}

.library-card-issue .library-card-description {
    border-top-color: rgba(255, 255, 255, 0.28);
}

.library-card-issue .library-flag {
    color: var(--ink);
}

.library-card-issue .library-card-poster-image,
.library-card-issue .library-card-poster-empty {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35), 0 2px 8px rgba(0, 0, 0, 0.45);
}

/* The blood-coloured delete button disappears against a blood-coloured
   card, so on a flagged card every control is outlined in white instead. */
.library-card-issue .btn-void,
.library-card-issue .btn-blood {
    background: transparent !important;
    border: 1px solid rgba(255, 255, 255, 0.55) !important;
    color: var(--ink) !important;
}

.library-card-issue .btn-void:hover,
.library-card-issue .btn-blood:hover {
    background: var(--ink) !important;
    color: var(--blood) !important;
    border-color: var(--ink) !important;
}

/* ── Title cards ───────────────────────────────────────────────────────────── */
/* The library card as the movie and show listings draw it: a strip cut from
   the title's own page. The poster's colour lights the left of the card the
   way it lights the one-sheet, the archive's name is set heavy and close the
   way the sheet sets it, and the figures run between gold hairlines. It is
   still the reconciliation underneath: the archive's reading over IMDb's on
   one grid, so a disagreement reads as a broken column, and a card with
   anything wrong with it is still plainly red.

   Nothing on the card makes a stacking context. The light and the body are
   positioned and painted in the order they are written, so the Actions menu
   a card opens is drawn over the cards beneath it. */
.title-card {
    position: relative;
    margin-bottom: 0.85rem;
    background: rgba(0, 14, 25, 0.8);
    border: 1px solid rgba(201, 166, 52, 0.16);
    border-radius: 6px;
    box-shadow: 0 14px 36px rgba(0, 0, 0, 0.42);
    transition: border-color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
}

/* Under the pointer the card floods with the burgundy every highlighted row in
   this application warms to, its frame comes up in the same colour, and the
   light behind the poster brightens, which is how the row being read is found
   down a long listing. */
.title-card:hover {
    border-color: var(--card-edge-lift);
    background: var(--card-ground-lift);
    box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
}

.title-card:hover .title-card-glow::after {
    background: linear-gradient(90deg,
                                rgba(58, 16, 16, 0) 0%,
                                rgba(58, 16, 16, 0.55) 42%,
                                rgba(58, 16, 16, 0.9) 78%);
}

.title-card-glow {
    position: absolute;
    inset: 0;
    overflow: hidden;
    border-radius: inherit;
    pointer-events: none;
}

/* The poster blown up past the card's edge and blurred until only its colour
   is left, then sunk into the ground towards the right so the type stands on
   something steady. */
.title-card-light {
    position: absolute;
    top: -45%;
    left: -8%;
    width: 64%;
    height: 190%;
    object-fit: cover;
    filter: blur(42px) saturate(1.4);
    opacity: 0.26;
    transition: opacity 0.2s ease;
}

.title-card-glow::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg,
                                rgba(0, 14, 25, 0) 0%,
                                rgba(0, 14, 25, 0.55) 42%,
                                rgba(0, 14, 25, 0.88) 78%);
}

.title-card:hover .title-card-light {
    opacity: 0.38;
}

.title-card .library-card-body {
    position: relative;
    gap: 1.4rem;
    padding: 1.15rem 1.3rem;
}

/* The poster at the size it is recognised at rather than scanned at, framed in
   the gold hairline and deep shadow the one-sheet hangs its poster in. */
.title-card .library-card-poster-image,
.title-card .library-card-poster-empty {
    width: 5.6rem;
    border-radius: 3px;
    box-shadow: 0 0 0 1px rgba(201, 166, 52, 0.45), 0 14px 32px rgba(0, 0, 0, 0.6);
}

.title-card .library-card-poster-empty {
    box-shadow: inset 0 0 0 1px var(--line);
}

/* The two readings. Each is named in a quiet hand at the head of its row, and
   both years share one column in tabular figures, so a year that disagrees
   stands out of line. */
/* The title column is as wide as the longer of the two titles, so the years
   stand beside the names they belong to and the last column takes the slack. */
.title-card .library-reconcile {
    grid-template-columns: max-content minmax(0, max-content) max-content minmax(0, 1fr);
    column-gap: 0.85rem;
    row-gap: 0.35rem;
}

.title-card .library-reconcile-side {
    padding-top: 0;
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: rgba(247, 249, 251, 0.5);
}

/* The archive's name, set the way the title's own page sets it: heavy, tight
   and close, so a long name holds together as one shape. */
.title-card .library-card-title {
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.025em;
    text-wrap: balance;
}

.title-card .library-reconcile-year {
    font-family: inherit;
    font-variant-numeric: tabular-nums;
}

.title-card .library-card-title + .library-reconcile-year {
    font-size: 0.95rem;
    font-weight: 500;
    color: rgba(247, 249, 251, 0.62);
}

.title-card .library-reconcile-title {
    font-size: 0.92rem;
    font-weight: 600;
}

.title-card .library-reconcile-title + .library-reconcile-year {
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--gold);
}

.title-card .library-reconcile .is-absent {
    color: rgba(247, 249, 251, 0.4);
    font-weight: 400;
}

.title-card .library-reconcile .is-mismatch {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 var(--fault-edge);
}

/* The shape of a series as the one-sheet states its figures: one line, each
   figure divided from the next by a gold hairline. The line is drawn out to
   the left by one gutter and clipped there, so the hairline in front of
   whichever figure opens a wrapped line falls outside it. */
.title-card .library-card-shape:not(.library-card-genres) {
    gap: 0.35rem 0;
    margin: 0.8rem 0 0 -0.85rem;
    overflow: hidden;
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
    color: rgba(247, 249, 251, 0.78);
}

.title-card .library-card-shape:not(.library-card-genres) > span {
    margin-left: -1px;
    padding: 0 0.85rem;
    border-left: 1px solid rgba(201, 166, 52, 0.35);
    line-height: 1.15;
}

.title-card .library-card-shape-figure {
    font-family: inherit;
    font-weight: 600;
    color: var(--ink);
}

.title-card .library-card-shape-fault {
    font-family: inherit;
    font-weight: 600;
    color: var(--fault-edge);
}

/* The genres in the gold and weight the one-sheet sets its genres in. */
.title-card .library-card-genres {
    gap: 0.15rem 0.75rem;
    margin-top: 0.6rem;
    font-size: 0.84rem;
    font-weight: 600;
}

.title-card .library-card-genre {
    color: var(--gold);
}

.title-card .library-card-genre:hover,
.title-card .library-card-genre:focus {
    color: var(--ink);
    text-decoration: none;
    box-shadow: inset 0 -1px 0 currentColor;
}

.title-card .library-card-badges {
    margin-top: 0.75rem;
}

/* The billing, as the foot of the one-sheet carries it: the credit in a small
   light hand, the people heavy and close after it. The names IMDb ships with
   the title are set in ink, and the collected billing runs on beneath them in
   the gold of the IMDb reading, so the two depths of one answer stay told
   apart. */
.title-card .library-card-director {
    margin-top: 0.9rem;
    gap: 0.1rem 0.45rem;
    font-size: 0.84rem;
    color: rgba(247, 249, 251, 0.55);
}

.title-card .library-card-director-label {
    font-size: 0.74rem;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
}

.title-card .library-card-director-name {
    font-weight: 600;
    letter-spacing: -0.01em;
}

.title-card .library-card-cast {
    margin-top: 0.45rem;
    gap: 0.1rem 0.35rem;
    font-size: 0.84rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--ink);
}

/* Each name is a flex box, so the line break between a name and its closing
   tag is dropped and the comma sits against the name. */
.title-card .library-card-cast-name,
.title-card .library-card-director-name,
.title-card .library-card-billing-name {
    display: inline-flex;
}

.title-card .library-card-cast-name:not(:last-child)::after,
.title-card .library-card-director-name:not(:last-child)::after {
    color: rgba(247, 249, 251, 0.4);
}

.title-card .library-card-billing {
    margin-top: 0.3rem;
}

.title-card .library-card-billing-name {
    font-size: 0.8rem;
}

/* The synopsis as the one-sheet sets it: prose at reading size, on the open
   card rather than ruled off beneath it. */
.title-card .library-card-description {
    margin-top: 0.85rem;
    padding-top: 0;
    border-top: 0;
    max-width: 68ch;
    font-size: 0.9rem;
    line-height: 1.6;
    color: rgba(247, 249, 251, 0.84);
}

/* A title card with anything wrong with it. Still plainly red, which is what a
   maintainer scans a listing for, but lit by its poster the way every other
   card is: the red deepens across the card instead of standing flat. */
.title-card.library-card-issue {
    background: linear-gradient(100deg,
                                rgba(139, 0, 0, 0.62) 0%,
                                rgba(110, 6, 16, 0.86) 55%,
                                rgba(80, 8, 16, 0.92) 100%);
    border-color: rgba(255, 77, 77, 0.6);
}

.title-card.library-card-issue:hover {
    background: linear-gradient(100deg,
                                rgba(139, 0, 0, 0.62) 0%,
                                rgba(110, 6, 16, 0.86) 55%,
                                rgba(80, 8, 16, 0.92) 100%);
    border-color: var(--fault-edge);
}

.title-card.library-card-issue .title-card-glow::after {
    background: linear-gradient(90deg,
                                rgba(90, 6, 14, 0) 0%,
                                rgba(90, 6, 14, 0.5) 42%,
                                rgba(80, 8, 16, 0.85) 78%);
}

.title-card.library-card-issue .library-reconcile-title,
.title-card.library-card-issue .library-reconcile-year,
.title-card.library-card-issue .library-card-title + .library-reconcile-year,
.title-card.library-card-issue .library-reconcile-title + .library-reconcile-year,
.title-card.library-card-issue .library-card-genre,
.title-card.library-card-issue .library-card-shape-figure,
.title-card.library-card-issue .library-card-billing-name a,
.title-card.library-card-issue .library-card-description {
    color: var(--ink);
}

.title-card.library-card-issue .library-reconcile-side,
.title-card.library-card-issue .library-reconcile .is-absent,
.title-card.library-card-issue .library-card-director,
.title-card.library-card-issue .library-card-shape:not(.library-card-genres) {
    color: rgba(255, 255, 255, 0.72);
}

.title-card.library-card-issue .library-reconcile .is-mismatch {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 var(--ink);
}

/* The figure carries the hairline's padding, so the mark is drawn under the
   words rather than across the whole box. */
.title-card.library-card-issue .library-card-shape-fault {
    color: var(--ink);
    box-shadow: none;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

.title-card.library-card-issue .library-card-shape:not(.library-card-genres) > span {
    border-left-color: rgba(255, 255, 255, 0.35);
}

.title-card.library-card-issue .library-card-genre:hover,
.title-card.library-card-issue .library-card-genre:focus {
    color: var(--ink);
}

.title-card.library-card-issue .library-card-poster-image {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.4), 0 14px 32px rgba(0, 0, 0, 0.55);
}

@media (max-width: 767px) {
    .title-card .library-card-body {
        gap: 1rem;
        padding: 0.95rem 1rem;
    }

    .title-card .library-card-poster-image,
    .title-card .library-card-poster-empty {
        width: 4.5rem;
    }

    .title-card .library-card-title {
        font-size: 1.15rem;
    }

    .title-card .library-reconcile {
        grid-template-columns: max-content minmax(0, 1fr);
    }

    .title-card-light {
        width: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .title-card,
    .title-card-light {
        transition: none;
    }
}

/* ── Title listings ────────────────────────────────────────────────────────── */
/* The head of the movie and show listings, drawn as one card in the title
   page's frame: the library's name set the way the one-sheet sets a title,
   the readout as the sheet's figures between gold hairlines, the note in the
   sheet's prose, and the search and filters closing the card. The header and
   the note scroll away and the controls pin, so each part draws its own edge
   of the one frame, and all three stand on the same opaque ground the pinned
   part needs.

   The page is a T: the frame is the bar across the top, bleeding 1rem past
   the page's gutter on both sides, and the cards beneath it are the column at
   the page's own width. */
.title-listing-head {
    display: block;
    margin: 0 -1rem;
    padding: 1.7rem 1rem 0.4rem;
    background:
        radial-gradient(ellipse 55% 120% at 0% 0%,
                        rgba(201, 166, 52, 0.1), rgba(201, 166, 52, 0) 70%),
        #000e19;
    border: 1px solid rgba(201, 166, 52, 0.22);
    border-bottom: 0;
    border-radius: 6px 6px 0 0;
}

.title-listing-head .library-name {
    font-size: clamp(2rem, 4.2vw, 3rem);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.035em;
    text-transform: none;
    white-space: normal;
    color: var(--ink);
}

/* Each count stands over what it counts, divided from the next by a gold
   hairline. The line is drawn out to the left by one gutter and clipped there,
   so the hairline in front of whichever count opens a wrapped line falls
   outside it. */
.title-listing-head .library-readout {
    align-items: stretch;
    gap: 1rem 0;
    margin: 1.3rem 0 0 -1.4rem;
    overflow: hidden;
}

.title-listing-head .library-stat {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.3rem;
    margin-left: -1px;
    padding: 0 1.4rem;
    border-left: 1px solid rgba(201, 166, 52, 0.35);
}

.title-listing-head .library-stat-value {
    font-family: inherit;
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.02em;
    color: var(--ink);
}

.title-listing-head .library-stat-label {
    font-size: 0.74rem;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: rgba(247, 249, 251, 0.6);
}

/* Nothing wrong is good news, so a nought steps back. */
.title-listing-head .library-stat.is-zero .library-stat-value {
    font-weight: 500;
    color: rgba(247, 249, 251, 0.35);
}

.title-listing-head .library-stat.is-zero .library-stat-label {
    color: rgba(247, 249, 251, 0.4);
}

/* A count that is also the page it counts, in gold, the way every way onward
   here is marked. */
.title-listing-head a.library-stat-action .library-stat-value {
    color: var(--gold);
    box-shadow: none;
}

.title-listing-head a.library-stat-action:hover .library-stat-label,
.title-listing-head a.library-stat-action:focus-visible .library-stat-label {
    color: var(--gold);
    box-shadow: inset 0 -1px 0 currentColor;
}

.title-listing-head .library-stat.is-current .library-stat-value,
.title-listing-head .library-stat.is-current .library-stat-label {
    color: var(--gold);
}

/* What the listing holds, in the one-sheet's prose, at the band's own inset so
   it starts and stops on the same line as the readout above it and the search
   below it. */
.title-listing-note {
    margin: 0 -1rem;
    padding: 0.9rem 1rem 1.3rem;
    background: #000e19;
    border-left: 1px solid rgba(201, 166, 52, 0.22);
    border-right: 1px solid rgba(201, 166, 52, 0.22);
    font-size: 0.92rem;
    line-height: 1.6;
    color: rgba(247, 249, 251, 0.72);
}

.title-listing-controls {
    margin: 0 -1rem 1.25rem;
    padding: 1rem 1rem 0.2rem;
    background: #000e19;
    border: 1px solid rgba(201, 166, 52, 0.22);
    border-top-color: var(--line);
    border-radius: 0 0 6px 6px;
    box-shadow: 0 14px 36px rgba(0, 0, 0, 0.42);
}

.title-listing-controls .library-search {
    gap: 0.6rem;
    margin-bottom: 0.9rem;
}

.title-listing-controls .library-search .form-control {
    padding: 0.65rem 2.4rem 0.65rem 1rem;
    font-size: 0.95rem;
    background: rgba(255, 255, 255, 0.04) !important;
    border: 1px solid rgba(201, 166, 52, 0.22) !important;
    border-radius: 4px;
}

.title-listing-controls .library-search .form-control:focus {
    border-color: var(--gold) !important;
    box-shadow: 0 0 0 3px rgba(201, 166, 52, 0.18) !important;
}

.title-listing-controls .library-search-clear {
    right: 0.9rem;
}

.title-listing-controls .library-search .btn {
    align-self: stretch;
    padding: 0 1.4rem;
    border-radius: 4px;
    font-size: 0.8rem;
}

/* The genres and the interests, named in the sheet's quiet hand. */
.title-listing-controls .library-genres-label {
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    color: rgba(247, 249, 251, 0.72);
}

.title-listing-controls .library-genres-chosen {
    font-size: 0.8rem;
}

.title-listing-controls .library-genres-tags {
    font-size: 0.82rem;
    line-height: 2;
}

.title-listing-controls .library-genres-tag {
    border-radius: 3px;
    color: rgba(247, 249, 251, 0.62);
}

.title-listing-controls .library-genres-tag:hover,
.title-listing-controls .library-genres-tag:focus-visible {
    color: var(--gold);
}

.title-listing-controls .library-genres-all {
    font-size: 0.74rem;
    letter-spacing: 0;
    text-transform: none;
    border-color: rgba(201, 166, 52, 0.3);
}

.title-listing-controls .library-genres-tag.is-on {
    background: var(--gold);
    border-color: var(--gold);
    color: var(--void);
}

.title-listing-controls a.library-genres-tag.is-on:hover,
.title-listing-controls a.library-genres-tag.is-on:focus-visible {
    background: #b8952e;
    color: var(--void);
}

/* The index, in the sheet's own face with tabular figures, each initial a
   rounded key. */
.title-listing-controls .alpha-seek {
    gap: 0.15rem;
    padding-bottom: 0.9rem;
}

.title-listing-controls .alpha-seek-letter {
    min-width: 1.7rem;
    padding: 0.35rem 0.4rem;
    border-radius: 3px;
    font-family: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    text-align: center;
    color: var(--gold);
}

/* An initial nothing is filed under, greyed, so the alphabet keeps its shape
   and the gap reads as information. */
.title-listing-controls .alpha-seek-letter.is-empty {
    opacity: 0.4;
    color: var(--steel);
}

.title-listing-controls .alpha-seek-all {
    margin-right: 0.6rem;
    padding: 0.35rem 0.7rem;
    border: 1px solid rgba(201, 166, 52, 0.3);
    font-size: 0.78rem;
    letter-spacing: 0;
    text-transform: none;
    color: var(--steel);
}

.title-listing-controls a.alpha-seek-letter:hover,
.title-listing-controls a.alpha-seek-letter:focus-visible {
    background: rgba(201, 166, 52, 0.14);
    color: var(--gold);
}

.title-listing-controls a.alpha-seek-all:hover,
.title-listing-controls a.alpha-seek-all:focus-visible {
    border-color: var(--gold);
}

.title-listing-controls a.alpha-seek-letter.is-active,
.title-listing-controls .alpha-seek-all.is-active {
    background: var(--gold);
    border-color: var(--gold);
    color: var(--void);
}

/* How many to a page and which of them are showing, in the sheet's labels. */
.title-listing-pager .library-pager-label {
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: rgba(247, 249, 251, 0.6);
}

.title-listing-pager .list-per-page-select {
    border-radius: 4px;
}

.title-listing-pager .library-pager-count {
    font-family: inherit;
    font-variant-numeric: tabular-nums;
    font-size: 0.82rem;
    color: rgba(247, 249, 251, 0.6);
}

@media (max-width: 767px) {
    .title-listing-head {
        padding: 1.2rem 1rem 0.3rem;
    }

    .title-listing-head .library-readout {
        margin-left: -1rem;
    }

    .title-listing-head .library-stat {
        padding: 0 1rem;
    }

    .title-listing-head .library-stat-value {
        font-size: 1.25rem;
    }

    .title-listing-note {
        padding: 0.8rem 1rem 1.1rem;
    }

    .title-listing-controls {
        padding: 0.8rem 1rem 0.1rem;
    }
}

/* ── A header with its control, and a note closing the frame ──────────────── */
/* A page whose header carries the control that makes something (a channel, a
   news channel): the header held to one line with the control at its far
   end. Where nothing pins beneath the note, the note closes the frame. */
.title-listing-head:has(.library-header-action) {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem 1.6rem;
    padding: 0.9rem 1rem;
}

.title-listing-head:has(.library-header-action) .library-name {
    font-size: 1.75rem;
}

.title-listing-head:has(.library-header-action) .library-readout {
    margin: 0 0 0 -1.4rem;
}

.title-listing-head:has(.library-header-action) .library-stat-value {
    font-size: 1.2rem;
}

.title-listing-head .library-header-action {
    padding: 0.45rem 1.1rem !important;
    border-radius: 4px;
    font-size: 0.8rem !important;
}

.title-listing-note.library-header-standalone {
    margin-bottom: 1.25rem;
    border-bottom: 1px solid rgba(201, 166, 52, 0.22);
    border-radius: 0 0 6px 6px;
    box-shadow: 0 14px 36px rgba(0, 0, 0, 0.42);
}

/* A header with nothing beneath it in the frame closes the frame itself. */
.title-listing-head.library-header-standalone {
    margin-bottom: 1.25rem;
    border-bottom: 1px solid rgba(201, 166, 52, 0.22);
    border-radius: 6px;
    box-shadow: 0 14px 36px rgba(0, 0, 0, 0.42);
}

.title-listing-head.library-header-standalone:not(:has(.library-header-action)) {
    padding-bottom: 1.2rem;
}

/* ── A page's views, in the frame ──────────────────────────────────────────── */
/* The readings a page can be read as (what is waiting and what is marked
   special, the requests and what has become of them), under the counts and in
   the sheet's quiet hand, the way the genres and the interests are named in
   the controls below. The view being read is marked the way every reading in
   force here is: gold. */
.title-listing-head .library-sort {
    gap: 0.3rem 1.3rem;
    margin-top: 1.2rem;
    padding-bottom: 0.7rem;
}

.title-listing-head .library-sort-option {
    gap: 0.45rem;
    padding-bottom: 0.2rem;
    font-size: 0.92rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    text-transform: none;
    color: rgba(247, 249, 251, 0.62);
}

.title-listing-head a.library-sort-option:hover,
.title-listing-head a.library-sort-option:focus {
    color: var(--gold);
}

.title-listing-head .library-sort-option.is-current {
    color: var(--gold);
}

/* How many each view holds, beside its name in the title page's figures. */
.title-listing-head .library-sort-count {
    margin-left: 0;
    font-family: inherit;
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
    font-weight: 500;
    color: rgba(247, 249, 251, 0.5);
}

.title-listing-head .library-sort-option.is-current .library-sort-count,
.title-listing-head a.library-sort-option:hover .library-sort-count {
    color: var(--gold);
}

@media (max-width: 767px) {
    .title-listing-head .library-sort {
        margin-top: 1rem;
    }
}

/* ── Channels made, one card each ──────────────────────────────────────────── */
/* The channels and the news channels as they are managed, each framed the way
   a library card is: the same ground and gold hairline, flooding burgundy
   under the pointer. Its number leads it in the gold the channel selector
   writes numbers in, set heavy and close with its name; how it plays is said
   in small framed marks beside the name, and what it draws on closes it in
   the title page's figures. */
.channel-manage-list .channel-row {
    gap: 1rem;
    padding: 1rem 1.2rem;
    margin-bottom: 0.7rem;
    background: rgba(0, 14, 25, 0.8);
    border: 1px solid rgba(201, 166, 52, 0.16);
    border-radius: 6px;
    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.38);
    transition: border-color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
}

.channel-manage-list .channel-row:hover {
    border-color: var(--card-edge-lift);
    background: var(--card-ground-lift);
    box-shadow: 0 16px 38px rgba(0, 0, 0, 0.5);
}

.channel-manage-list .channel-row-mark {
    padding-top: 0.2rem;
    color: rgba(201, 166, 52, 0.7);
    font-size: 1.05rem;
}

.channel-manage-list .channel-row-heading {
    gap: 0.35rem 0.7rem;
}

.channel-manage-list .channel-row-number {
    color: var(--gold);
    font-variant-numeric: tabular-nums;
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.channel-manage-list .channel-row-title {
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

.channel-manage-list .channel-row-random,
.channel-manage-list .channel-row-filler {
    padding: 0.1rem 0.45rem;
    border: 1px solid rgba(201, 166, 52, 0.3);
    border-radius: 3px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    color: rgba(247, 249, 251, 0.75);
}

.channel-manage-list .channel-row-reading {
    margin-top: 0.3rem;
    font-size: 0.9rem;
    color: rgba(247, 249, 251, 0.78);
}

.channel-manage-list .channel-row-description {
    margin-top: 0.5rem;
    max-width: 68ch;
    font-size: 0.9rem;
    line-height: 1.6;
    color: rgba(247, 249, 251, 0.84);
}

.channel-manage-list .channel-row-meta {
    gap: 0.3rem 0.9rem;
    margin-top: 0.65rem;
}

.channel-manage-list .channel-row-fact {
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
}

.channel-manage-list .channel-row-pool {
    font-family: inherit;
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
    font-weight: 600;
}

.channel-manage-list .channel-row-who,
.channel-manage-list .channel-row-when {
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
    color: rgba(247, 249, 251, 0.6);
}

/* Every control in one voice, the destructive one keeping its colour. */
.channel-manage-list .channel-row-controls .btn {
    text-transform: uppercase;
}

@media (prefers-reduced-motion: reduce) {
    .channel-manage-list .channel-row {
        transition: none;
    }
}

/* ── Season cards ──────────────────────────────────────────────────────────── */
/* One row per season of one show. Shorter than a library card and set to one
   line, because a page of seasons is read down the left edge as a sequence:
   the number leads in the figure face, what is in that season follows, and
   anything wrong with it is marked at the right. */
.season-card {
    display: flex;
    align-items: baseline;
    gap: 1.1rem;
    background: var(--card-ground);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.35rem;
    padding: 0.55rem 1.1rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.season-card:hover {
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
    background: var(--card-ground-lift);
}

/* The number is also the control that renames the directory, since the number
   is what a wrongly-named one gets wrong. It stays a plain figure until it is
   reached for, so the row reads as a sequence rather than as a rank of
   buttons. */
.season-card-number {
    flex: 0 0 auto;
    min-width: 6.5rem;
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-figure);
    font-weight: 600;
    color: var(--ink);
    transition: color 0.12s ease, box-shadow 0.12s ease;
}

button.season-card-number {
    cursor: pointer;
    box-shadow: inset 0 -2px 0 transparent;
}

button.season-card-number:hover,
button.season-card-number:focus {
    color: var(--gold);
    box-shadow: inset 0 -2px 0 var(--gold);
    outline: none;
}

/* The rest of the row opens the season. A link rather than a control, because
   it only shows what is already there. */
.season-card-detail {
    flex: 1 1 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.9rem;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

a.season-card-detail:hover,
a.season-card-detail:focus {
    text-decoration: none;
}

/* The directory as it exists, shown where it says more than the number does.
   Set in the figure face because it is a string being checked character by
   character when something about it is wrong. The name renames the thing; it
   is the one part of a card that is a control in its own right whether or not
   the card opens anything. */
.season-card-directory {
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--card-fact);
    color: var(--steel);
    word-break: break-word;
}

.season-card-state {
    font-size: var(--card-mark);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
}

/* A directory that is not a season opens into a listing of what is in it. The
   card is the target and its controls take their own clicks, so the pointer
   changes over the card rather than over the whole row of buttons. */
.season-card-open {
    cursor: pointer;
}

.season-card-open:hover {
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
    background: var(--card-ground-lift);
}

button.season-card-directory {
    cursor: pointer;
}

button.season-card-directory:hover,
button.season-card-directory:focus {
    color: var(--gold);
    text-decoration: underline;
    outline: none;
}

/* Controls sitting inside a card, holding their own ground rather than
   inheriting the card's click: a season's Actions menu, or the controls a
   directory or file loose in a show carries.
   Stacked, the way every other card in this application stacks its controls,
   and held to the top so the column starts on the card's own line however
   far the card runs beneath it. */
.season-card-controls {
    flex: 0 0 auto;
    align-self: flex-start;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.25rem;
    min-width: 9.5rem;
}

/* Inside the column each control is a full-width row of it, so the labels line
   up down the left edge rather than each button being its own width. */
.season-card-controls .btn {
    margin: 0 !important;
    width: 100%;
    text-align: left;
}

.season-card-move,
.season-card-promote,
.season-card-queue,
.season-card-report,
.season-card-controls .btn {
    flex: 0 0 auto;
    font-size: var(--card-control);
    padding: var(--card-control-pad);
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* A season already lined up. Inert because what it would do is done, so it
   reads as a state rather than as an action taken away. */
.season-card-queue.is-queued {
    border-color: rgba(201, 166, 52, 0.5) !important;
    color: var(--gold) !important;
    opacity: 1;
}

.season-card-queue.is-queued:disabled {
    opacity: 1;
    pointer-events: none;
}

.season-card-stray .season-card-number {
    min-width: 2rem;
    color: var(--steel);
}

@media (max-width: 575px) {
    .season-card {
        flex-wrap: wrap;
        gap: 0.3rem 1.1rem;
    }
    .season-card-number {
        min-width: 0;
    }
}

/* ── One season's episodes ─────────────────────────────────────────────────── */
/* The files as they are named on disk, beside what they weigh. Set as rows
   rather than cards: this is a directory listing, and it is read against the
   file browser it mirrors, so the filename carries the whole line in the
   figure face and nothing competes with it. */
.season-directory-note {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin: 0 0 1rem;
}

/* The files answering for no slot, set off from the sequence above them
   because they are not part of it. */
.episode-group-heading {
    margin: 1.5rem 0 0.5rem;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--steel);
}

/* The two that change the order. Square, because an arrow is the whole label
   and a word beside it would say the same thing twice. */
.queue-row-step {
    min-width: 2rem;
    text-align: center;
}

/* Something already on the queue. The control is inert because the thing it
   would do is done, so it reads as a state rather than as an action that has
   been taken away. */
.library-card-queue.is-queued,
.episode-row-queue.is-queued {
    border-color: rgba(201, 166, 52, 0.5) !important;
    color: var(--gold) !important;
    opacity: 1;
}

.library-card-queue.is-queued:disabled,
.episode-row-queue.is-queued:disabled {
    opacity: 1;
    pointer-events: none;
}

.library-card-issue .library-card-queue.is-queued {
    border-color: rgba(255, 255, 255, 0.55) !important;
    color: var(--ink) !important;
}

/* Playing something at once, wherever it is offered: a film's card, an
   episode's row, a season's. It is the one control on a card that carries the
   filled accent, because it is the one that acts immediately. */
.library-card-play,
.episode-row-play,
.season-card-play {
    font-size: var(--card-control) !important;
    padding: var(--card-control-pad) !important;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

.episode-row-play,
.season-card-play {
    flex: 0 0 auto;
    align-self: flex-start;
    margin: 0.5rem 0.85rem 0.5rem 0;
}

.season-card-play {
    margin: 0;
}

/* The queue control closes an episode row, held to the title's line so it does
   not stretch down a card that runs three lines deep. */
.episode-row-queue {
    flex: 0 0 auto;
    align-self: flex-start;
    margin: 0.5rem 0.85rem 0.5rem 0;
    font-size: var(--card-control) !important;
    padding: var(--card-control-pad) !important;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* A slot with no file behind it keeps the control and greys it, so the row
   holds the same shape as the ones around it and the absence reads as this
   episode not being here rather than as a missing feature. */
.episode-row-queue:disabled {
    opacity: 0.35;
    cursor: default;
}

/* ── Cast names ────────────────────────────────────────────────────────────── */
/* A line of performers where every name is a search of the library for that
   performer. Defined once because every listing in this application carries
   one, and a name has to look and behave the same on all of them: a plain
   name until it is reached for, then the accent.

   The line carries the layout and the calling page's own class carries the
   size and colour, so a cast reads as part of the card it sits on. */
.cast-line {
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.1rem 0.3rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Carried on the element as well as the class, so that it outranks the
   calling page's own class for this line. Several of those set display block
   (they were written for a span) and a block list puts every performer on a
   line of their own. Their size, colour and spacing still apply; only the
   direction of the line is settled here. */
ul.cast-line {
    display: flex;
}

.cast-line-name {
    white-space: nowrap;
}

.cast-line-name:not(:last-child)::after {
    content: ',';
}

.cast-line-name a {
    color: inherit;
    text-decoration: none;
}

.cast-line-name a:hover,
.cast-line-name a:focus {
    color: var(--gold);
    text-decoration: underline;
}

.cast-line-name a:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* ── A performer's name ────────────────────────────────────────────────────── */
/* What a name on a card opens: the two pages it could mean, at the pointer.
   Drawn on the same slate and hairline every record here is, and small: it is
   read in the moment it appears and then gone. */
.cast-menu {
    position: fixed;
    z-index: 1090;
    min-width: 13rem;
    padding: 0.5rem;
    background: var(--slate);
    border: 1px solid var(--line);
    border-left: 3px solid var(--gold);
    border-radius: 3px;
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.7);
}

/* Who was clicked, so a menu that opened over a dense listing still says what
   it is about. */
.cast-menu-name {
    display: block;
    padding: 0.15rem 0.5rem 0.4rem;
    font-size: var(--card-fact);
    font-weight: 600;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cast-menu-action {
    display: block;
    padding: 0.35rem 0.5rem;
    border-radius: 2px;
    font-size: var(--card-control);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--steel);
    text-decoration: none;
    transition: background 0.12s ease, color 0.12s ease;
}

.cast-menu-action:hover,
.cast-menu-action:focus-visible {
    background: var(--void);
    color: var(--gold);
    text-decoration: none;
}

.cast-menu-action:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
}

@media (prefers-reduced-motion: reduce) {
    .cast-menu-action {
        transition: none;
    }
}

/* The widths a bar can take, one to a whole percent: the same figure the
   share beside it is stated in, so a bar and its number cannot disagree. A
   width comes from a class because this application does not put style
   attributes in its markup. */
.console-fill-0   { width: 0%; }
.console-fill-1   { width: 1%; }
.console-fill-2   { width: 2%; }
.console-fill-3   { width: 3%; }
.console-fill-4   { width: 4%; }
.console-fill-5   { width: 5%; }
.console-fill-6   { width: 6%; }
.console-fill-7   { width: 7%; }
.console-fill-8   { width: 8%; }
.console-fill-9   { width: 9%; }
.console-fill-10  { width: 10%; }
.console-fill-11  { width: 11%; }
.console-fill-12  { width: 12%; }
.console-fill-13  { width: 13%; }
.console-fill-14  { width: 14%; }
.console-fill-15  { width: 15%; }
.console-fill-16  { width: 16%; }
.console-fill-17  { width: 17%; }
.console-fill-18  { width: 18%; }
.console-fill-19  { width: 19%; }
.console-fill-20  { width: 20%; }
.console-fill-21  { width: 21%; }
.console-fill-22  { width: 22%; }
.console-fill-23  { width: 23%; }
.console-fill-24  { width: 24%; }
.console-fill-25  { width: 25%; }
.console-fill-26  { width: 26%; }
.console-fill-27  { width: 27%; }
.console-fill-28  { width: 28%; }
.console-fill-29  { width: 29%; }
.console-fill-30  { width: 30%; }
.console-fill-31  { width: 31%; }
.console-fill-32  { width: 32%; }
.console-fill-33  { width: 33%; }
.console-fill-34  { width: 34%; }
.console-fill-35  { width: 35%; }
.console-fill-36  { width: 36%; }
.console-fill-37  { width: 37%; }
.console-fill-38  { width: 38%; }
.console-fill-39  { width: 39%; }
.console-fill-40  { width: 40%; }
.console-fill-41  { width: 41%; }
.console-fill-42  { width: 42%; }
.console-fill-43  { width: 43%; }
.console-fill-44  { width: 44%; }
.console-fill-45  { width: 45%; }
.console-fill-46  { width: 46%; }
.console-fill-47  { width: 47%; }
.console-fill-48  { width: 48%; }
.console-fill-49  { width: 49%; }
.console-fill-50  { width: 50%; }
.console-fill-51  { width: 51%; }
.console-fill-52  { width: 52%; }
.console-fill-53  { width: 53%; }
.console-fill-54  { width: 54%; }
.console-fill-55  { width: 55%; }
.console-fill-56  { width: 56%; }
.console-fill-57  { width: 57%; }
.console-fill-58  { width: 58%; }
.console-fill-59  { width: 59%; }
.console-fill-60  { width: 60%; }
.console-fill-61  { width: 61%; }
.console-fill-62  { width: 62%; }
.console-fill-63  { width: 63%; }
.console-fill-64  { width: 64%; }
.console-fill-65  { width: 65%; }
.console-fill-66  { width: 66%; }
.console-fill-67  { width: 67%; }
.console-fill-68  { width: 68%; }
.console-fill-69  { width: 69%; }
.console-fill-70  { width: 70%; }
.console-fill-71  { width: 71%; }
.console-fill-72  { width: 72%; }
.console-fill-73  { width: 73%; }
.console-fill-74  { width: 74%; }
.console-fill-75  { width: 75%; }
.console-fill-76  { width: 76%; }
.console-fill-77  { width: 77%; }
.console-fill-78  { width: 78%; }
.console-fill-79  { width: 79%; }
.console-fill-80  { width: 80%; }
.console-fill-81  { width: 81%; }
.console-fill-82  { width: 82%; }
.console-fill-83  { width: 83%; }
.console-fill-84  { width: 84%; }
.console-fill-85  { width: 85%; }
.console-fill-86  { width: 86%; }
.console-fill-87  { width: 87%; }
.console-fill-88  { width: 88%; }
.console-fill-89  { width: 89%; }
.console-fill-90  { width: 90%; }
.console-fill-91  { width: 91%; }
.console-fill-92  { width: 92%; }
.console-fill-93  { width: 93%; }
.console-fill-94  { width: 94%; }
.console-fill-95  { width: 95%; }
.console-fill-96  { width: 96%; }
.console-fill-97  { width: 97%; }
.console-fill-98  { width: 98%; }
.console-fill-99  { width: 99%; }
.console-fill-100 { width: 100%; }

/* ── Dashboard ─────────────────────────────────────────────────────────────── */
/* The page is one card on the same slate ground every record in this
   application sits on. The room behind the page is background: nothing is
   asked to be read against it. Inside, the sections are ruled apart by the
   hairline this application divides everything with, so it stays one surface
   rather than becoming a set of tiles. */
.board {
    padding: 0;
    overflow: hidden;
}

.board-gauges {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
    gap: 1.7rem 3rem;
    padding: 1.5rem 1.6rem 1.7rem;
    border-bottom: 1px solid var(--line);
}

.gauge {
    margin: 0;
    min-width: 0;
}

.gauge-caption {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0 0.6rem;
    margin-bottom: 0.7rem;
}

/* What is being measured, in the micro-caps this application names a reading
   with everywhere else. */
.gauge-name {
    flex: 0 0 100%;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--steel);
    margin-bottom: 0.35rem;
}

/* The whole the strip is a length of. The largest figure on any page here, and
   set in the same tabular face as the smallest, because on this page every
   number is an instrument reading. */
.gauge-total {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 2.6rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--ink);
}

.gauge-unit {
    font-size: 0.76rem;
    color: var(--steel);
}

/* How much of that whole is in the state the strip is about, held to the far
   end of the caption line so it lands over the end of the strip below it: the
   reading the whole measure exists to give, and the only thing on the page
   that states a share on its own. */
.gauge-lead {
    margin-left: auto;
    display: inline-flex;
    align-items: baseline;
    gap: 0.35rem;
    text-align: right;
}

.gauge-lead-share {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 1.15rem;
    font-weight: 600;
    line-height: 1;
    color: var(--gold);
}

.gauge-lead-label {
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--steel);
}

/* The strip itself. Its unexposed length is the page's own ground, so a
   half-filled strip reads as film that has not been shot rather than as an
   empty box, and the perforations are punched in the colour of the card
   underneath so they read as holes through it. */
.gauge-strip {
    position: relative;
    display: flex;
    height: 2.4rem;
    border-radius: 2px;
    overflow: hidden;
    background: var(--void);
    box-shadow: inset 0 0 0 1px var(--line);
}

.gauge-strip::before,
.gauge-strip::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 0.34rem;
    z-index: 1;
    background-image: repeating-linear-gradient(
        to right,
        var(--slate) 0 0.3rem,
        transparent 0.3rem 0.78rem);
}

.gauge-strip::before { top: 0; }
.gauge-strip::after { bottom: 0; }

.gauge-band {
    width: var(--band);
    min-width: 2px;
    transition: width 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* Four states, four weights of the same accent. Gold is what this application
   marks a good reading with, so the described stretch carries it solid, the
   half-done stretch carries it thinned, what is not there at all recedes to
   the ground, and what has left the disk takes the fault colour. */
.gauge-band-full { background: var(--gold); }
.gauge-band-part { background: rgba(201, 166, 52, 0.42); }
.gauge-band-none { background: rgba(95, 110, 124, 0.32); }
.gauge-band-fault { background: var(--fault-edge); }

.gauge-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 1.4rem;
    margin-top: 0.7rem;
}

.gauge-key {
    display: inline-flex;
    align-items: baseline;
    gap: 0.35rem;
    white-space: nowrap;
}

/* A key is marked by the colour of the stretch it names, on the figure alone:
   a swatch beside every one would be four more objects saying what four
   figures already say. */
.gauge-key-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--steel);
}

.gauge-key-full .gauge-key-value { color: var(--gold); }
.gauge-key-part .gauge-key-value { color: rgba(201, 166, 52, 0.75); }
.gauge-key-fault .gauge-key-value { color: var(--fault-edge); }

.gauge-key-label {
    font-size: 0.74rem;
    color: var(--steel);
}

.gauge-key-share {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 0.72rem;
    color: rgba(247, 249, 251, 0.4);
}

/* What the strip is a length of. A bar whose whole is not stated is a bar
   nobody can check, and these two are the page's argument. */
.gauge-note {
    max-width: 52ch;
    margin: 0.7rem 0 0;
    font-size: 0.68rem;
    line-height: 1.45;
    color: rgba(95, 110, 124, 0.85);
}

/* ── The sheet ───────────────────────────────────────────────────────────── */
/* One surface, ruled into groups. The group stands in the margin and its
   readings run beside it, which is the shape a library card uses to set two
   readings of one title against each other: the same device at page scale. */
.sheet {
    display: block;
}

.sheet-group {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    column-gap: 2.5rem;
    padding: 1.05rem 1.6rem;
    border-bottom: 1px solid var(--line);
    transition: background 0.12s ease;
}

/* A group lights faintly under the pointer, the way every row in this
   application does: enough to keep your place across a wide sheet, and no
   more than that. */
.sheet-group:hover {
    background: #1f1f1f;
}

.sheet-group:last-child {
    border-bottom: 0;
}

.sheet-side {
    margin: 0;
    min-width: 7.5rem;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--steel);
    padding-top: 0.3rem;
}

.sheet-side a {
    color: var(--steel);
    text-decoration: none;
}

.sheet-side a:hover,
.sheet-side a:focus-visible {
    color: var(--gold);
}

.sheet-readings {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 0.1rem 2rem;
    min-width: 0;
}

.sheet-reading {
    display: grid;
    grid-template-columns: 5rem minmax(0, 1fr);
    align-items: baseline;
    column-gap: 0.85rem;
    padding: 0.16rem 0;
}

/* A reading carrying an explanation is taller than one line, so its figure
   holds to the top rather than floating against the middle of the sentence. */
.sheet-reading:has(.sheet-note) {
    align-items: start;
    padding: 0.45rem 0;
}

/* The figures line up in one column down the whole sheet, so a value out of
   place is findable without reading a word. */
.sheet-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.4;
    text-align: right;
    color: var(--ink);
}

/* The label and, under it, what the figure counts. They share the column so
   the figures stay in one line down the sheet however long an explanation
   runs. */
.sheet-naming {
    min-width: 0;
}

.sheet-label {
    display: block;
    font-size: 0.76rem;
    line-height: 1.4;
    color: var(--steel);
    text-decoration: none;
    min-width: 0;
}

/* What was counted to arrive at the figure. Set below the label and quieter
   than it: the sheet is read by its numbers, and this is here for the moment
   somebody stops and asks what one of them means. Held to a measure so it
   reads as a sentence rather than as a line stretched across the page. */
.sheet-note {
    display: block;
    max-width: 46ch;
    margin-top: 0.1rem;
    font-size: 0.68rem;
    line-height: 1.45;
    color: rgba(95, 110, 124, 0.85);
}

a.sheet-label:hover,
a.sheet-label:focus-visible {
    color: var(--gold);
    text-decoration: underline;
}

/* A figure asking for something. Marked only when it is not nought: nought of
   anything on this sheet is the good outcome, and a sheet where everything is
   marked marks nothing. */
.sheet-reading.is-marked .sheet-value {
    color: var(--gold);
}

.sheet-side a:focus-visible,
a.sheet-label:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

@media (max-width: 767px) {
    .board-gauges {
        padding: 1.2rem 1.1rem 1.3rem;
    }
    .sheet-group {
        grid-template-columns: minmax(0, 1fr);
        row-gap: 0.5rem;
        padding: 1rem 1.1rem;
    }
    .gauge-total {
        font-size: 2rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .sheet-group {
        transition: none;
    }
}

/* The strips fill once as the page settles, which is the archive being
   measured. Nothing else on the page moves. */
@media (prefers-reduced-motion: no-preference) {
    .gauge-band {
        animation: gauge-fill 0.75s cubic-bezier(0.22, 0.61, 0.36, 1) both;
    }
    .gauge:last-child .gauge-band {
        animation-delay: 0.14s;
    }
}

@keyframes gauge-fill {
    from { width: 0; }
    to { width: var(--band); }
}

/* ── Requests ──────────────────────────────────────────────────────────────── */
/* Two lists on one page, and they have to read as different things: what IMDb
   answered, which is a question waiting to be picked from, and what has been
   asked for, which is the record. The results carry the gold edge of something
   actionable; the requests are cards like every other record here. */
.request-result,
.request-row {
    display: flex;
    align-items: stretch;
    background: var(--card-ground);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.35rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.request-result:hover,
.request-row:hover {
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
    background: var(--card-ground-lift);
}

.request-result-art,
.request-row-art {
    flex: 0 0 auto;
    padding: 0.7rem 0 0.7rem 0.9rem;
}

.request-result-poster,
.request-result-poster-empty,
.request-row-poster,
.request-row-poster-empty {
    width: 3.5rem;
    border-radius: 2px;
    display: block;
    box-shadow: 0 0 0 1px rgba(201, 166, 52, 0.25), 0 2px 8px rgba(0, 0, 0, 0.45);
}

.request-result-poster,
.request-row-poster {
    height: auto;
    background: var(--void);
}

.request-result-poster-empty,
.request-row-poster-empty {
    aspect-ratio: 2 / 3;
    background: var(--void);
    color: var(--steel);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    box-shadow: inset 0 0 0 1px var(--line);
}

.request-result-body,
.request-row-body {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.7rem 0.95rem;
}

.request-result-heading,
.request-row-heading {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.1rem 0.8rem;
    line-height: 1.4;
}

.request-result-title,
.request-row-title {
    font-size: var(--card-name);
    font-weight: 600;
    color: var(--ink);
    word-break: break-word;
}

.request-result-fact,
.request-row-fact {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    color: var(--steel);
    white-space: nowrap;
}

/* IMDb's own word for what the title is, and the part of the archive a title
   of that kind belongs in: which is what somebody filling the request needs
   to know before they go looking. */
.request-result-kind,
.request-row-kind {
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--line);
    border-radius: 2px;
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
}

.request-result-section,
.request-row-section {
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--gold);
    white-space: nowrap;
}

.request-result-description,
.request-row-description {
    margin: 0.35rem 0 0;
    max-width: var(--card-prose-measure);
    font-size: var(--card-prose);
    line-height: var(--card-prose-leading);
    color: rgba(247, 249, 251, 0.68);
}

/* Who asked and when, which is what makes this a shared list rather than a
   pile of titles. */
.request-row-meta {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.1rem 0.9rem;
    margin-top: 0.45rem;
}

.request-row-who {
    font-size: var(--card-mark);
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
}

.request-row-when,
.request-row-imdb,
.request-result-imdb {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 0.74rem;
    color: var(--steel);
    text-decoration: none;
}

a.request-row-imdb:hover,
a.request-result-imdb:hover {
    color: var(--gold);
    text-decoration: underline;
}

.request-result-controls,
.request-row-controls {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.3rem;
    padding: 0.7rem 0.9rem 0.7rem 0;
    min-width: 9.5rem;
}

.request-result-controls .btn,
.request-row-controls .btn {
    text-align: left;
    font-size: var(--card-control);
    padding: var(--card-control-pad);
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* Asked for. The result stays on screen so the rest of the search can be
   worked through, and the control says the thing it would do is done: the
   same state the same act takes on every other page here. */
.request-result-ask.is-requested {
    background: transparent !important;
    border: 1px solid rgba(201, 166, 52, 0.5) !important;
    color: var(--gold) !important;
    opacity: 1;
    pointer-events: none;
}

.request-row-controls .btn:disabled {
    opacity: 0.35;
}

@media (max-width: 575px) {
    .request-result-art,
    .request-row-art {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .request-result,
    .request-row {
        transition: none;
    }
}

/* ── Filmography ───────────────────────────────────────────────────────────── */
/* Somebody's career rather than the archive's holdings, so the cards are read
   for what is missing as much as for what is there. Same card language as
   everywhere else: the difference is that most of these rows will never be in
   the library, and the ones that already are say so instead of offering a
   control that would do nothing. */
.person-card {
    display: flex;
    align-items: stretch;
    background: var(--card-ground);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.4rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.person-card-open {
    cursor: pointer;
}

.person-card-open:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* One thing somebody acted in, and not in the archive, which is most of a
   career. Held to an outline, the way everything this application knows about
   but does not have is drawn: a missing season, an episode with no file, a
   candidate nobody has asked for yet. The row is a space where a holding could
   be. */
.credit-card {
    display: flex;
    align-items: stretch;
    background: transparent;
    border: 1px dashed var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.4rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.credit-card:hover {
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
}

/* In the archive. Filled and solid, which is how every record this
   application actually holds is drawn: the card carries weight because there
   is something behind it. */
.credit-card-held {
    background: var(--card-ground);
    border-style: solid;
    border-left-color: rgba(201, 166, 52, 0.5);
}

.credit-card-held:hover {
    background: var(--card-ground-lift);
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
}

.credit-card-art {
    flex: 0 0 auto;
    padding: 0.7rem 0 0.7rem 0.9rem;
}

.credit-card-poster,
.credit-card-poster-empty {
    width: 3rem;
    border-radius: 2px;
    display: block;
    box-shadow: 0 0 0 1px rgba(201, 166, 52, 0.25), 0 2px 8px rgba(0, 0, 0, 0.45);
}

.credit-card-poster {
    height: auto;
    background: var(--void);
}

.credit-card-poster-empty {
    aspect-ratio: 2 / 3;
    background: var(--void);
    color: var(--steel);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8rem;
    box-shadow: inset 0 0 0 1px var(--line);
}

.credit-card-body {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.7rem 0.95rem;
}

/* The series an episode belongs to, above the episode's own name: that is the
   part somebody recognises. */
.credit-card-series {
    display: block;
    font-size: var(--card-mark);
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
}

.credit-card-heading {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.1rem 0.8rem;
    line-height: 1.4;
}

.credit-card-title {
    font-size: var(--card-name);
    font-weight: 600;
    color: var(--ink);
    word-break: break-word;
}

.credit-card-fact {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    color: var(--steel);
    white-space: nowrap;
}

.credit-card-kind {
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--line);
    border-radius: 2px;
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
}

.credit-card-section {
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--gold);
    white-space: nowrap;
}

/* Who they played, which is half of what a filmography is read for. */
.credit-card-character {
    display: block;
    margin-top: 0.2rem;
    font-size: var(--card-fact);
    color: var(--ink);
}

.credit-card-description {
    margin: 0.3rem 0 0;
    max-width: var(--card-prose-measure);
    font-size: var(--card-prose);
    line-height: var(--card-prose-leading);
    color: rgba(247, 249, 251, 0.68);
}

.credit-card-meta {
    display: block;
    margin-top: 0.3rem;
}

.credit-card-imdb {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.74rem;
    color: var(--steel);
    text-decoration: none;
}

a.credit-card-imdb:hover {
    color: var(--gold);
    text-decoration: underline;
}

.credit-card-controls {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.3rem;
    padding: 0.7rem 0.9rem 0.7rem 0;
    min-width: 9.5rem;
}

.credit-card-controls .btn {
    text-align: left;
    font-size: var(--card-control);
    padding: var(--card-control-pad);
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* Asked for. Inert, the way every control in this application is once the
   thing it would do is done. */
.credit-card-request.is-requested {
    background: transparent !important;
    border: 1px solid rgba(201, 166, 52, 0.5) !important;
    color: var(--gold) !important;
    opacity: 1;
    pointer-events: none;
}

@media (max-width: 767px) {
    .person-card-art,
    .credit-card-art {
        display: none;
    }
    .credit-card-controls {
        min-width: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .person-card,
    .credit-card {
        transition: none;
    }
}

/* A suggestion asked for. The control is inert because the thing it would do
   is done, so it reads as a state rather than as an action taken away. */
.suggestion-card-request.is-requested {
    background: transparent !important;
    border: 1px solid rgba(201, 166, 52, 0.5) !important;
    color: var(--gold) !important;
    opacity: 1;
    pointer-events: none;
}

/* ── Issues ────────────────────────────────────────────────────────────────── */
/* What people have found wrong with what the archive holds, on the same card
   language the queue is read in: this is the other list a person works down,
   and the two should read the same way. What differs is the gutter and the
   body: the gutter says which level a fault sits at rather than a place in an
   order, and the first thing in the body is the sentence somebody typed, since
   that is the reason the row exists. */
.issue-row {
    display: flex;
    align-items: stretch;
    background: var(--card-ground);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.35rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.issue-row:hover {
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
    background: var(--card-ground-lift);
}

/* The fault itself, as it was typed. Set at full contrast and ruled off from
   the rest of the row, because everything else on it describes the thing and
   this one line describes what is wrong with it. Line breaks are kept: a
   person listing three faults writes three lines. */
.issue-row-problem {
    margin: 0.45rem 0 0;
    padding-left: 0.7rem;
    border-left: 2px solid rgba(201, 166, 52, 0.45);
    max-width: var(--card-prose-measure);
    font-size: var(--card-prose);
    line-height: var(--card-prose-leading);
    color: var(--ink);
    white-space: pre-wrap;
    word-break: break-word;
}

/* A row that opens the report on its own page. The card is the target and its
   controls take their own clicks, so the pointer changes over the card rather
   than over the row of buttons at the end of it. */
.issue-row-open {
    cursor: pointer;
}

.issue-row-open:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* ── One report ────────────────────────────────────────────────────────────── */
/* Where a fault is worked out. The same card the listing draws a row on, opened
   up: the artwork is the size a poster is looked at rather than scanned, the
   fault is set as prose because it is the thing being read, and the
   conversation runs beneath it. */
.issue-detail {
    display: flex;
    align-items: stretch;
    background: var(--slate);
    border: 1px solid var(--line);
    border-left: 3px solid var(--gold);
    border-radius: 3px;
    margin-bottom: 0.35rem;
}

/* The fault itself, set as prose. Line breaks are kept, because a person
   listing three faults writes three lines. */
.issue-detail-problem {
    margin: 0.8rem 0 0;
    padding-left: 0.8rem;
    border-left: 2px solid rgba(201, 166, 52, 0.45);
    max-width: var(--card-prose-measure);
    font-size: var(--record-prose);
    line-height: 1.55;
    color: var(--ink);
    white-space: pre-wrap;
    word-break: break-word;
}

/* The field a fault or a comment is written in. Taller than one line because
   either takes a sentence or three, and free to grow downwards only: a
   control that can be dragged wider than its own dialog is a control fighting
   its box. */
.issue-report-field {
    min-height: 6rem;
    resize: vertical;
}

@media (prefers-reduced-motion: reduce) {
    .issue-row {
        transition: none;
    }
}

/* ── Watch ─────────────────────────────────────────────────────────────────── */
/* The one page in this application that is not a listing. Everything else here
   is built for comparing fields; this is built for looking at a picture, so the
   screen takes the width it can get, sits on void rather than on slate, and the
   controls hold to the bottom edge of the picture instead of floating beside
   it. */
/* The stage is two different things at two moments, and says so. Idle it is
   part of the console: the void ground with the title card mounted on it.
   Playing it goes black and the picture owns the page. Nothing else in this application changes ground like that, which is
   how the page marks the difference between choosing and watching. */
/* It closes the frame the page's header opens: the same gold hairline down
   both sides and across the foot, seamed to the header above it by the
   hairline everything here is divided by, and rounded at its foot. The frame
   clips the picture to its corners. */
.watch-stage {
    margin: 0 -1rem 1.25rem;
    background: #000;
    border: 1px solid rgba(201, 166, 52, 0.22);
    border-top-color: var(--line);
    border-radius: 0 0 6px 6px;
    overflow: hidden;
    box-shadow: 0 14px 36px rgba(0, 0, 0, 0.42);
    transition: background 0.2s ease;
}

/* ── The title card ──────────────────────────────────────────────────────── */
/* What is cued, as the card a film gets before it starts. The poster is
   mounted in the same hairline gold frame every other poster in this
   application wears, because it is the same object at a different size, and
   the room behind it stays visible, so the page reads as the console it is
   rather than as somebody's streaming front end. */
.watch-cue {
    display: flex;
    align-items: center;
    gap: 2rem;
    padding: 2.25rem 1rem;
    min-height: 18rem;
}

.watch-cue-art {
    flex: 0 0 auto;
}

.watch-cue-poster,
.watch-cue-poster-empty {
    width: 10rem;
    border-radius: 3px;
    display: block;
    box-shadow: 0 0 0 1px rgba(201, 166, 52, 0.3), 0 10px 34px rgba(0, 0, 0, 0.6);
}

.watch-cue-poster {
    height: auto;
    background: var(--void);
}

.watch-cue-body {
    flex: 1 1 auto;
    min-width: 0;
    max-width: 46rem;
}

/* Where this sits in the run: first up, or part-way through. Set in the
   micro-caps this application gives every label that names what a reading is. */
.watch-cue-eyebrow {
    display: block;
    font-size: var(--record-eyebrow);
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--gold);
}

/* The name of the one thing this page is about, set at the record step: the
   same step the report on its own page is titled at, and a rung above the rows
   beneath it. */
.watch-cue-title {
    margin: 0.35rem 0 0;
    font-size: var(--record-name);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.25;
    color: var(--ink);
    word-break: break-word;
}

.watch-cue-episode {
    display: block;
    margin-top: 0.3rem;
    font-size: var(--record-second);
    font-weight: 600;
    color: var(--ink);
}

.watch-cue-facts {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.1rem 1rem;
    margin-top: 0.55rem;
}

.watch-cue-fact {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--record-fact);
    color: var(--steel);
    white-space: nowrap;
}

/* The one control on the card, and the only filled gold button on the page.
   Everything else here is a hairline, so where the eye lands is where playing
   starts. */
.watch-cue-start {
    display: inline-flex;
    align-items: center;
    margin-top: 1.25rem;
    padding: 0.6rem 1.6rem;
    border: 0;
    border-radius: 3px;
    background: var(--gold);
    color: var(--void);
    font-family: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 0.12s ease;
}

.watch-cue-start:hover {
    background: #b8952e;
}

.watch-cue-start:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 3px;
}

/* Where the film was left part-way through there are two intentions, and going
   back to the start is the quieter of them: it is set as a line of type beside
   the control that acts, the way this application sets a second answer beside a
   first everywhere else. */
.watch-cue-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1.1rem;
}

.watch-cue-note {
    margin: 0.5rem 0 0;
    max-width: 46ch;
    font-size: var(--record-prose);
    line-height: 1.6;
    color: var(--steel);
}

/* Waiting on the archive for more of the file. A ring rather than a spinner
   of dots, turning slowly, because a stall on a large file lasts long enough
   that anything faster reads as alarm. */
.watch-wait {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}

.watch-wait-mark {
    width: 2.6rem;
    height: 2.6rem;
    border: 2px solid rgba(255, 255, 255, 0.18);
    border-top-color: var(--gold);
    border-radius: 50%;
    animation: watch-wait-turn 0.9s linear infinite;
}

@keyframes watch-wait-turn {
    to { transform: rotate(360deg); }
}

/* What a player says about the picture arriving too slowly: held to the top
   right of the picture, clear of the routing marker at its left, on the slate
   and hairline every panel over the picture is drawn on. The On Demand player
   and the channels both carry it. It takes no pointer: it is a reading rather
   than a control, and the click meant for the picture lands underneath. */
.watch-pace {
    position: absolute;
    top: 0.6rem;
    right: 0.75rem;
    z-index: 3;
    max-width: min(28rem, 60%);
    padding: 0.5rem 0.75rem;
    background: rgba(0, 10, 18, 0.82);
    border: 1px solid rgba(201, 166, 52, 0.35);
    border-radius: 4px;
    font-size: var(--card-fact);
    line-height: 1.45;
    color: var(--ink);
    pointer-events: none;
}

.watch-pace[hidden] {
    display: none;
}

/* The controls sit on the picture, on a gradient rather than a bar, so the
   frame keeps its full height and the controls still read against a bright
   scene. They stay put while paused, and while something is playing they stay
   for as long as the pointer keeps moving; a few seconds of stillness is
   somebody watching rather than somebody reaching for a control, and the
   picture is what they came for. Any movement brings them back.

   A control reached by keyboard keeps them, because a keyboard is a pointer
   that does not move, which the script decides, since a control clicked with
   a mouse holds focus exactly as one tabbed to does, and the bar would then
   stay up for ever after somebody pressed Play. */
.watch-controls {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    /* Above the plate rather than under it. A plate says one thing and offers
       the way off it; the transport is how somebody does anything else, and it
       should not go away because the player is talking. */
    z-index: 4;
    padding: 2.5rem 1rem 0.7rem;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.88), rgba(0, 0, 0, 0));
    opacity: 1;
    transition: opacity 0.18s ease;
}

/* Where the film actually is. The played length says roughly how far in you
   are; from a sofa, roughly is all a four-pixel bar can say, so the head says
   exactly, and it grows under the pointer, because that is the moment
   somebody is aiming at it. */
.watch-scrub-head {
    position: absolute;
    top: 50%;
    left: 0;
    width: 0.55rem;
    height: 0.55rem;
    margin: -0.275rem 0 0 -0.275rem;
    border-radius: 50%;
    background: var(--gold);
    box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.35);
    pointer-events: none;
    transition: width 0.12s ease, height 0.12s ease, margin 0.12s ease;
}

/* One line, always. A control that will not fit leaves for the menu at the end
   of the bar rather than wrapping onto a second row; a transport that is two
   rows deep on one window and one on the next is a transport whose controls
   are never where they were last time. */
.watch-buttons {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 0.65rem;
}

.watch-button {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 0;
    border-radius: 3px;
    background: none;
    color: var(--ink);
    font-size: 0.9rem;
    cursor: pointer;
    transition: color 0.12s ease, background 0.12s ease;
}

.watch-button:hover,
.watch-button:focus-visible {
    color: var(--gold);
    background: rgba(255, 255, 255, 0.08);
    outline: none;
}

.watch-button:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 1px;
}

.watch-button:disabled {
    opacity: 0.35;
    cursor: default;
}

/* Captions, when they are on. The one control on the bar that holds a state. */
.watch-button.is-on {
    color: var(--gold);
    box-shadow: inset 0 -2px 0 var(--gold);
}

/* The second half of the clock is the control that turns it over: how long the
   film is, or how much of it is left. It stays set as the figure it is, so the
   line reads as a clock rather than as a reading with a button stuck to it. */
.watch-time-turn {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-variant-numeric: tabular-nums;
    color: inherit;
    cursor: pointer;
    box-shadow: inset 0 -1px 0 transparent;
    transition: color 0.12s ease, box-shadow 0.12s ease;
}

/* What is playing, named on the bar so the picture never has to be paused to
   find out. Takes the room left between the transport and the volume, and
   gives it up first. */
.watch-now {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding: 0 0.5rem;
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.72);
}

.watch-volume {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.watch-volume-input {
    width: 5rem;
    accent-color: var(--gold);
    cursor: pointer;
}

/* ── Choosing a subtitle ─────────────────────────────────────────────────── */
/* Candidates from the provider. Built on the same box the IMDb assign results
   use, because it is the same act: several answers to one question, and one of
   them gets written to disk. */
.subtitle-modal-file {
    margin: 0 0 0.9rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8rem;
    color: var(--steel);
    word-break: break-all;
}

.subtitle-result-list {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    max-height: 50vh;
    overflow-y: auto;
}

.subtitle-result {
    background: var(--void);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.subtitle-result:hover,
.subtitle-result:focus-within {
    border-color: var(--gold);
    background: rgba(201, 166, 52, 0.08);
}

/* Matched on the file's own bytes rather than on its name, so it is timed to
   this exact release. The one distinction worth marking in a list where every
   other difference is a matter of judgement. */
.subtitle-result.is-exact {
    border-left-color: var(--gold);
}

.subtitle-result-choose {
    display: block;
    width: 100%;
    padding: 0.55rem 0.8rem;
    border: 0;
    background: none;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.subtitle-result-choose:focus {
    outline: none;
}

.subtitle-result-choose:disabled {
    opacity: 0.5;
    pointer-events: none;
}

.subtitle-result-title {
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--ink);
    word-break: break-word;
}

.subtitle-result-meta {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.1rem 0.85rem;
    margin-top: 0.2rem;
}

.subtitle-result-fact {
    font-size: 0.74rem;
    color: var(--steel);
    white-space: nowrap;
}

.subtitle-result-exact {
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--gold);
    white-space: nowrap;
}

/* Fetching subtitles for a file. The provider's hash is asked first whatever
   the file is called, so this is one click and no modal wherever an exact
   match exists. */
.episode-row-subtitles,
.episode-row-retitle,
.episode-row-report {
    flex: 0 0 auto;
    align-self: flex-start;
    margin: 0.5rem 0.85rem 0.5rem 0;
    font-size: var(--card-control) !important;
    padding: var(--card-control-pad) !important;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* An exact match landed and was written. Marked and inert, because the thing
   it would do is done. */
.episode-row-subtitles.is-taken {
    border-color: rgba(201, 166, 52, 0.5) !important;
    color: var(--gold) !important;
    opacity: 1;
}

.episode-row-subtitles.is-taken:disabled {
    opacity: 1;
    pointer-events: none;
}

/* Pinned to the top of the viewport while the page scrolls, as the letter
   index is on the listings: the page's name and readout first, the player
   directly beneath it, and the row that names the queue beneath the player
   while its cards pass under. The script keeps --watch-header-height and
   --watch-stage-height at the drawn heights of the header and the player. All
   three carry an opaque ground, because the page background is a fixed
   gradient the cards would otherwise show through. */
.watch > .library-header {
    position: sticky;
    top: 0;
    z-index: 21;
}

/* A queue row, framed the way a library card is: the same ground and gold
   hairline, lit from behind by its own art. Nothing on it makes a stacking
   context, so the Actions menu a row opens is drawn over the rows beneath. */
.watch-item {
    position: relative;
    display: flex;
    align-items: stretch;
    background: rgba(0, 14, 25, 0.8);
    border: 1px solid rgba(201, 166, 52, 0.16);
    border-radius: 6px;
    margin-bottom: 0.7rem;
    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.38);
    transition: border-color 0.16s ease, background 0.16s ease, box-shadow 0.16s ease;
}

@media (max-width: 767px) {
    .watch-cue {
        flex-direction: column;
        align-items: flex-start;
        gap: 1.25rem;
        padding: 1.5rem 1rem;
    }
    .watch-cue-poster,
    .watch-cue-poster-empty {
        width: 7rem;
    }
}

@media (max-width: 575px) {
    .watch-now {
        display: none;
    }
    .watch-volume-input {
        width: 3.5rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .watch-item,
    .watch-stage,
    .watch-controls,
    .watch-button,
    .watch-cue-start,
    .watch-scrub-head,
    .watch-time-turn {
        transition: none;
    }
    .watch-wait-mark {
        animation: none;
    }
}

/* The control bar is ten controls on one line, laid out for a mouse. On a
   phone the volume slider and the name of what is playing take the room the
   transport needs and the row spills off the picture.

   Both give way rather than being shrunk: a phone carries its own volume keys,
   and what is playing is named on the header above the picture and again on
   the row that is marked in the queue below it. What is left is the transport,
   the clock and the four controls that have nowhere else to be, and it is
   allowed to run onto a second line rather than being squeezed into one. */
/* Which controls are on the bar is settled by measuring it rather than by a
   width written down here: the player is a panel above a queue on one page and
   the whole of a screen on the next, and the same window holds a different
   amount of bar in each. What this width decides is the size of what is left
   and how tight it is packed. */
@media (max-width: 767px) {
    .watch-controls {
        padding: 2rem 0.5rem 0.5rem;
    }

    .watch-buttons {
        gap: 0.3rem;
    }

    .watch-now {
        display: none;
    }

    /* A thumb covers a control where a pointer lands on one. */
    .watch-button {
        width: 2.3rem;
        height: 2.3rem;
        font-size: 0.95rem;
    }
}

/* A window with less height left over than the bar takes at its full size. The
   bar gives up what is drawn for comfort rather than for use: the gradient it
   fades in over, the room around the scrub, and a millimetre off every control.
   What is left is the scrub and one row, which is what the picture's floor is
   measured from. */
@media (max-height: 308px) {
    .watch-controls {
        padding: 0.5rem 1rem 0.4rem;
        background: rgba(0, 0, 0, 0.88);
    }

    .watch-button {
        width: 1.7rem;
        height: 1.7rem;
        font-size: 0.8rem;
    }
}

@media (max-width: 575px) {

    /* The title card carries a poster and a paragraph side by side, which at
       this width leaves neither enough room to be read. Stacked, the poster
       stays the mounted still it is and the words get the full measure. */
    .watch-cue {
        flex-direction: column;
        align-items: flex-start;
        gap: 1.1rem;
        padding: 1.4rem 0.9rem;
        min-height: 0;
    }

    .watch-cue-poster,
    .watch-cue-poster-empty {
        width: 7rem;
    }

}

/* ── IMDb assignment results ───────────────────────────────────────────────── */
/* Candidate titles inside the assign modal. Each is a click target, so the
   whole row lifts on hover rather than relying on a button inside it. */
.assign-result-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-height: 55vh;
    overflow-y: auto;
}

/* The box holds two actions: choosing the candidate, and opening its IMDb
   title page. The box carries the frame and the hover so the row still reads
   as one thing, while each action stays its own element. */
.assign-result {
    background: var(--void);
    border: 1px solid var(--line);
    border-radius: 3px;
    padding: 0.6rem 0.8rem;
    color: var(--ink);
    transition: border-color 0.12s ease, background 0.12s ease;
}

.assign-result:hover,
.assign-result:focus-within {
    border-color: var(--gold);
    background: rgba(201, 166, 52, 0.08);
}

.assign-result-choose {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.assign-result-choose:focus {
    outline: none;
}

.assign-result-choose:disabled {
    opacity: 0.5;
    pointer-events: none;
}

.assign-result-title {
    font-weight: 600;
    font-size: 0.95rem;
}

.assign-result-year {
    color: var(--gold);
    font-variant-numeric: tabular-nums;
    margin-left: 0.4rem;
}

/* IMDb's classification of the candidate, carried on every row. */
.assign-result-kind {
    display: inline-block;
    margin-left: 0.5rem;
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--line);
    border-radius: 2px;
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
    vertical-align: 0.1rem;
}

/* A candidate of a kind this listing does not hold. It keeps full contrast
   on the kind and the section it belongs to, because those two are the whole
   reason it is on screen; the title recedes to steel and the row loses its
   hover, so the rows that can be chosen are the ones that answer the
   pointer. */
.assign-result.is-unassignable {
    background: transparent;
    border-style: dashed;
}

.assign-result.is-unassignable .assign-result-choose:disabled {
    opacity: 1;
    cursor: default;
    pointer-events: auto;
}

.assign-result.is-unassignable:hover,
.assign-result.is-unassignable:focus-within {
    border-color: var(--line);
    background: transparent;
}

.assign-result.is-unassignable .assign-result-title {
    color: var(--steel);
}

.assign-result.is-unassignable .assign-result-kind {
    border-color: rgba(201, 166, 52, 0.4);
    color: var(--gold);
}

.assign-result-note {
    margin-top: 0.2rem;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--gold);
}

.assign-result-meta {
    color: var(--steel);
    font-size: 0.8rem;
    margin-top: 0.15rem;
}

/* The IMDb id, and the way to read about a candidate before committing to
   it. Inline so the link is only as wide as the id itself, leaving the rest
   of the line to the button behind it. */
.assign-result-id {
    display: inline-block;
    margin-top: 0.15rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.8rem;
    color: var(--steel);
}

a.assign-result-id:hover,
a.assign-result-id:focus {
    color: var(--gold);
    text-decoration: underline;
}

a.assign-result-id:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* An entry waiting on the next background pass. The control is inert (the
   request is already in) so it signals by pulsing instead, which is the one
   thing on a card that says something is going to happen rather than
   something is wrong. Kept to the accent and to a slow cycle: this can sit on
   screen for a couple of minutes, and anything faster would be an alarm. */
.library-card-refresh.is-pending {
    border-color: var(--gold) !important;
    color: var(--gold) !important;
    opacity: 1;
    animation: refresh-pending 1.6s ease-in-out infinite;
}

/* The disabled rule dims every other inert control on a card, and this one is
   inert for a different reason: it is working, not unavailable. */
.library-card-refresh.is-pending:disabled {
    opacity: 1;
    pointer-events: none;
}

@keyframes refresh-pending {
    0%, 100% {
        border-color: rgba(201, 166, 52, 0.3);
        color: rgba(201, 166, 52, 0.5);
    }
    50% {
        border-color: var(--gold);
        color: var(--gold);
    }
}

/* A pulse that will not stop is exactly what someone asking for reduced
   motion is asking not to have, so it holds at its brightest instead. */
@media (prefers-reduced-motion: reduce) {
    .library-card-refresh.is-pending {
        animation: none;
    }
}

/* ── The card's Actions menu ───────────────────────────────────────────────── */
/* Everything past the queue control, behind one control. The listing is read
   down its cards rather than across their buttons, so the two used while
   reading stay open and the rest is opened when there is something to do.
   The controls inside are the card's own controls (same classes, same
   endpoints) laid out as menu rows here rather than written twice. */
.library-card-actions {
    display: block;
}

.library-card-actions > .dropdown-toggle {
    width: 100%;
    text-align: left;
}

.library-card-actions .dropdown-menu {
    min-width: 12rem;
    padding: 0.25rem;
}

/* A control inside the menu is a row of it: full width, ruled off from the
   next, and carrying no frame of its own: the menu is the frame. Every row is
   set in one size and one voice, whatever kind of button it is and wherever
   the menu sits, so the rows are one height and read as one list. */
.library-card-actions .dropdown-menu .btn {
    display: block;
    width: 100%;
    margin: 0;
    text-align: left;
    background: transparent !important;
    border: 0 !important;
    border-radius: 2px;
    padding: 0.4rem 0.6rem !important;
    font-size: var(--card-control) !important;
    font-weight: 600;
    line-height: 1.5;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

.library-card-actions .dropdown-menu .btn + .btn {
    border-top: 1px solid var(--line) !important;
}

.library-card-actions .dropdown-menu .btn:hover:not(:disabled) {
    background: var(--void) !important;
    color: var(--gold) !important;
}

/* The control that acts at once (playing an episode or a season now) loses the
   filled ground it carries on a card, and its dark lettering with it, so it is
   set in the accent that ground was. */
.library-card-actions .dropdown-menu .btn-gold {
    color: var(--gold) !important;
}

/* The one control that destroys something keeps its colour, because that is
   what it is for. */
.library-card-actions .dropdown-menu .btn-blood {
    color: var(--fault-edge) !important;
}

.library-card-actions .dropdown-menu .btn-blood:hover:not(:disabled) {
    background: var(--blood) !important;
    color: var(--ink) !important;
}

/* On a flagged card the palette is forced to white, and the menu sits on its
   own ground rather than on the red. */
.library-card-issue .library-card-actions .dropdown-menu {
    background: var(--slate) !important;
}

.library-card-issue .library-card-actions .dropdown-menu .btn {
    color: var(--ink) !important;
    border: 0 !important;
}

.library-card-issue .library-card-actions .dropdown-menu .btn + .btn {
    border-top: 1px solid var(--line) !important;
}

/* Per-card control panel, held to the right edge. Stays in the layout even
   when it holds nothing, so the detail column ends at the same place on
   every card and the listing does not ripple. */
.library-card-controls {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.3rem;
    min-width: 9.5rem;
}

.library-card-controls .btn {
    text-align: left;
    font-size: var(--card-control);
    padding: var(--card-control-pad);
    letter-spacing: 0.04em;
}

/* Below the breakpoint the three columns stack: controls run along the
   bottom, and the reconciliation drops its id column under the titles
   rather than forcing a horizontal scroll. */
@media (max-width: 767px) {
    .library-card-body {
        flex-wrap: wrap;
    }
    .library-card-controls {
        flex-direction: row;
        flex-wrap: wrap;
        width: 100%;
        min-width: 0;
    }
    .library-reconcile {
        grid-template-columns: max-content minmax(0, 1fr);
    }
    .library-reconcile-id {
        grid-column: 2;
    }
}

.library-card-year {
    color: var(--gold);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* The IMDb title id, shown as the link to the title page. Rendered in a
   monospace face because it is an identifier a maintainer reads and
   compares character by character when checking a mis-pinned entry. */
.library-card-imdb {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
    letter-spacing: 0.02em;
}

/* ── Poster showcase ──────────────────────────────────────────────────────── */
/* The artwork on a card is a thumbnail sized for scanning a listing of
   thousands. This is where a poster is shown as a poster: the page drops to
   void, the art takes the screen at the largest size that fits, and the
   cursor carries the zoom-out shape everywhere, because a click anywhere
   closes it. */
.library-card-poster-zoom {
    display: block;
    padding: 0;
    border: 0;
    background: none;
    border-radius: 2px;
    cursor: zoom-in;
}

.poster-showcase {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1080;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.1rem;
    padding: 2rem 1.5rem;
    background: rgba(0, 10, 18, 0.95);
    cursor: zoom-out;
}

.poster-showcase.is-open {
    display: flex;
    animation: poster-showcase-enter 170ms ease-out;
}

.poster-showcase-frame {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.85rem;
    margin: 0;
    min-height: 0;
}

/* The gold hairline the cards use, carried onto the full-size art so a poster
   with dark edges still resolves against the field behind it. */
.poster-showcase-image {
    display: block;
    max-width: 92vw;
    max-height: 80vh;
    border-radius: 2px;
    background: var(--void);
    box-shadow: 0 0 0 1px rgba(201, 166, 52, 0.45), 0 24px 60px rgba(0, 0, 0, 0.75);
}

.poster-showcase-caption {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 0.6rem;
    text-align: center;
}

.poster-showcase-title {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--ink);
}

.poster-showcase-year {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    color: var(--gold);
    font-variant-numeric: tabular-nums;
}

.poster-showcase-year:empty {
    display: none;
}

/* The layer carries no close button, so the way out is said once in the
   listing's own micro-type. */
.poster-showcase-dismiss {
    margin: 0;
    font-size: 0.62rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--steel);
}

@keyframes poster-showcase-enter {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

/* The art comes forward as it arrives, which is the difference between a
   poster being put on display and an overlay appearing. */
.poster-showcase.is-open .poster-showcase-frame {
    animation: poster-showcase-lift 170ms ease-out;
}

@keyframes poster-showcase-lift {
    from {
        transform: scale(0.94);
    }
    to {
        transform: none;
    }
}

/* The page behind the showcase holds its place while a poster is open. */
body.poster-showcase-open {
    overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
    .poster-showcase.is-open,
    .poster-showcase.is-open .poster-showcase-frame {
        animation: none;
    }
}

/* ── Media conversion ──────────────────────────────────────────────────────── */
/* The queue and the history of what has been rewritten, on the same card
   language every other list here is read in. What differs is that a row can be
   working while it is being looked at, so the row carries a bar rather than a
   state word alone. */
/* Four columns rather than one stretched body: what is being converted, where
   the work has got to, and what can be done about it, each with a place of its
   own. Held in one flexible column the whole row packed against the left edge
   and the width had nowhere to go: a listing of long rows with an empty half.
   The slack now falls between the name and the reading rather than after both.
   The two right-hand columns take a width rather than hold one. Given room
   they open to the size the bar and the buttons want; given less they close
   down to the size those still work at, so the row keeps its shape all the way
   down instead of rearranging itself part of the way. */
.conversion-row {
    display: grid;
    grid-template-columns:
        3rem minmax(0, 1fr) minmax(10rem, 19rem) minmax(7rem, 9.5rem);
    align-items: start;
    background: var(--card-ground);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.35rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

/* The bar. One element in two lives: a proportion while the job runs, and the
   word it settled on afterwards. It takes the width of its column rather than
   a width of its own; a bar is the one reading here that is better for being
   longer, since a percent of it is a pixel either way. */
.conversion-progress {
    display: flex;
    align-items: center;
    gap: 0.7rem;
}

.conversion-progress-track {
    flex: 1 1 auto;
    display: flex;
    height: 0.5rem;
    border-radius: 2px;
    overflow: hidden;
    background: var(--void);
    box-shadow: inset 0 0 0 1px var(--line);
}

.conversion-progress-fill {
    background: var(--gold);
    transition: width 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* The word the bar resolves to, under it rather than beside it; beside it the
   bar had to give up width to a label that is the same six characters on every
   row of the page. */
.conversion-progress-label {
    display: block;
    margin-top: 0.35rem;
    font-size: var(--card-mark);
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
}

/* The figure beside the bar, in the face this application gives every reading.
   Tabular, so a number climbing through the tens and hundreds does not shift
   the word next to it, and fixed enough in width that the row holds still while
   it counts. */
.conversion-progress-percent {
    flex: 0 0 auto;
    min-width: 2.8rem;
    text-align: right;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    font-weight: 600;
    color: var(--gold);
    white-space: nowrap;
}

/* A settled row says its answer in the word alone. */
.conversion-progress-percent:empty {
    display: none;
}

/* The four endings. Each is the same bar carrying a different answer, so the
   colour is on the word rather than on a badge beside it. */
.conversion-progress[data-state="running"] .conversion-progress-label {
    color: var(--gold);
}

.conversion-progress[data-state="converted"] .conversion-progress-label {
    color: var(--gold);
}

.conversion-progress[data-state="converted"] .conversion-progress-fill {
    width: 100%;
}

.conversion-progress[data-state="error"] .conversion-progress-label {
    color: var(--fault-edge);
}

.conversion-progress[data-state="canceled"] .conversion-progress-label,
.conversion-progress[data-state="refused"] .conversion-progress-label {
    color: var(--steel);
}

/* How much of a list to show at a time, and how much of it is on screen: the
   conversion history, the request list and both libraries. Set as one quiet
   line above the listing: it is a reading of the page rather than part of what
   the page is about. */
.conversion-controls,
.request-controls,
.library-pager {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.8rem;
    margin-bottom: 0.9rem;
}

.conversion-controls-label,
.request-controls-label,
.library-pager-label {
    margin: 0;
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--steel);
    white-space: nowrap;
}

.conversion-controls .list-per-page-select,
.request-controls .list-per-page-select,
.library-pager .list-per-page-select {
    width: auto;
    min-width: 5rem;
    padding: 0.2rem 0.5rem;
    font-size: var(--card-fact);
}

.conversion-controls-count,
.request-controls-count,
.library-pager-count {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    color: var(--steel);
}

/* Stepping through a list, closing the listing. The two ends and where you are
   between them, on the hairline language the rest of the page is ruled with;
   an end that leads nowhere stays in place and recedes, so the row keeps its
   shape at both ends of the list. */
.conversion-pagination,
.request-pagination,
.library-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1.4rem;
    margin-top: 1rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--line);
}

.conversion-page-step,
.request-page-step,
.library-page-step {
    font-size: var(--card-mark);
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--gold);
    text-decoration: none;
    white-space: nowrap;
}

a.conversion-page-step:hover,
a.conversion-page-step:focus,
a.request-page-step:hover,
a.request-page-step:focus,
a.library-page-step:hover,
a.library-page-step:focus {
    color: var(--gold);
    text-decoration: none;
    box-shadow: inset 0 -2px 0 var(--gold);
}

a.conversion-page-step:focus-visible,
a.request-page-step:focus-visible,
a.library-page-step:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

.conversion-page-step.is-spent,
.request-page-step.is-spent,
.library-page-step.is-spent {
    color: var(--steel);
    opacity: 0.45;
}

.conversion-page-where,
.request-page-where,
.library-page-where {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    color: var(--steel);
    white-space: nowrap;
}

/* ── The Convert control, once it is a bar ─────────────────────────────────── */
/* Converting takes minutes, so the control does not report and stop; it
   becomes the bar for the job it made. The button keeps its place among the
   card's controls and fills from the left as the work goes on, which is why the
   fill is drawn behind the label rather than beside it. */
.library-card-convert,
.episode-row-convert,
.season-card-convert {
    position: relative;
    overflow: hidden;
    font-size: var(--card-control) !important;
    padding: var(--card-control-pad) !important;
    letter-spacing: 0.04em;
    white-space: nowrap;
}

.episode-row-convert {
    flex: 0 0 auto;
    align-self: flex-start;
    margin: 0.5rem 0.85rem 0.5rem 0;
}

.conversion-button-fill {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    background: rgba(201, 166, 52, 0.28);
    transition: width 0.4s cubic-bezier(0.22, 0.61, 0.36, 1);
    pointer-events: none;
}

.conversion-button-label {
    position: relative;
}

/* Working. Inert because the thing it would do is being done. */
.library-card-convert.is-converting,
.episode-row-convert.is-converting,
.season-card-convert.is-converting {
    border-color: rgba(201, 166, 52, 0.5) !important;
    color: var(--gold) !important;
    opacity: 1;
}

.library-card-convert.is-converting:disabled,
.episode-row-convert.is-converting:disabled,
.season-card-convert.is-converting:disabled {
    opacity: 1;
    pointer-events: none;
}

/* Settled, one way or the other. */
.library-card-convert.is-fault,
.episode-row-convert.is-fault,
.season-card-convert.is-fault {
    border-color: var(--fault-edge) !important;
    color: var(--fault-edge) !important;
}

.library-card-convert.is-fault .conversion-button-fill,
.episode-row-convert.is-fault .conversion-button-fill,
.season-card-convert.is-fault .conversion-button-fill {
    background: rgba(255, 77, 77, 0.22);
}

/* On a flagged card the palette is forced to white, so the accent this control
   carries elsewhere would disappear into the red. */
.library-card-issue .library-card-convert.is-converting {
    border-color: rgba(255, 255, 255, 0.55) !important;
    color: var(--ink) !important;
}

@media (prefers-reduced-motion: reduce) {
    .conversion-row,
    .conversion-progress-fill,
    .conversion-button-fill {
        transition: none;
    }
}

.empty-state {
    color: var(--steel);
    text-align: center;
}

/* ── A maker's mark ────────────────────────────────────────────────────────── */
/* The name of a set, moulded into the band above it the way a brand is on the
   front of a television: the letters are the band's own dark plastic raised
   out of it, lit from above, so their top edges catch the light and their
   lower edges cast a short shadow back onto the band. Set small, heavy and
   wide, the size a brand is on a set rather than the size of a heading. The
   channels, On Demand and the news each carry one. */
.brand-mark {
    display: inline-block;
    /* The tracking is laid to the right of every letter, the last one
       included, so the name hangs that much off centre until the trailing
       step is taken back. */
    margin-right: -0.42em;
    font-weight: 700;
    letter-spacing: 0.42em;
    text-transform: uppercase;
    color: #1d3a50;
    text-shadow:
        0 -1px 0 rgba(247, 249, 251, 0.22),
        0 1px 1px rgba(0, 0, 0, 0.95),
        0 2px 3px rgba(0, 0, 0, 0.6);
}

/* Where the browser can paint through the type, the face of each letter is
   the moulding itself: lit along its top and falling into the band's dark
   towards its foot. The rims are drawn around the letters' own outline. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .brand-mark {
        background-image: linear-gradient(180deg,
                                          #3a5a72 0%,
                                          #1f3c52 45%,
                                          #0f2435 100%);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
        text-shadow: none;
        filter:
            drop-shadow(0 -1px 0 rgba(247, 249, 251, 0.2))
            drop-shadow(0 1px 1px rgba(0, 0, 0, 0.95))
            drop-shadow(0 2px 2px rgba(0, 0, 0, 0.5));
    }
}

/* ── A switch ──────────────────────────────────────────────────────────────── */
/* Something that is on or off, drawn as the switch it is: the reading beside
   the slider, named in the sheet's quiet hand. A switch in force is lit: its
   channel takes the gold and the knob in it glows, the way the dashboard's
   meters are lit. The whole control is the button, so the word is as
   pressable as the slider. User Management switches an account's roles with
   it, and the system page its background jobs. */
.account-switch {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 500;
    color: rgba(247, 249, 251, 0.55);
    cursor: pointer;
    transition: color 0.12s ease;
}

.account-switch:hover,
.account-switch.is-on {
    color: var(--ink);
}

.account-switch:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 3px;
    border-radius: 2px;
}

.account-switch-track {
    flex: 0 0 auto;
    position: relative;
    width: 2.2rem;
    height: 1.15rem;
    border: 1px solid rgba(201, 166, 52, 0.22);
    border-radius: 1rem;
    background: rgba(0, 0, 0, 0.45);
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.6);
    transition: border-color 0.12s ease;
}

.account-switch-track::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 50%;
    background: rgba(247, 249, 251, 0.35);
    transition: transform 0.12s ease, background 0.12s ease;
}

.account-switch.is-on .account-switch-track {
    border-color: var(--gold);
    background: rgba(201, 166, 52, 0.18);
}

.account-switch.is-on .account-switch-track::after {
    background: linear-gradient(180deg, #e2c35a 0%, var(--gold) 55%, #a8891f 100%);
    box-shadow: 0 0 8px rgba(201, 166, 52, 0.45);
    transform: translateX(1.05rem);
}

@media (prefers-reduced-motion: reduce) {
    .account-switch,
    .account-switch-track,
    .account-switch-track::after {
        transition: none;
    }
}

/* Confirm modal: themed Bootstrap modal used for destructive-action prompts */
.modal-content {
    background: var(--slate);
    border: 1px solid var(--line);
    color: var(--ink);
}

.modal-header {
    border-bottom: 1px solid var(--line);
}

.modal-footer {
    border-top: 1px solid var(--line);
}

.modal-title {
    color: var(--gold);
    font-size: 0.9rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* ── Smaller helper text below form fields (e.g. toggle explanations) ─────── */
.form-text-small {
    font-size: 0.8rem;
}

/* ── Search field and its button as one pill ──────────────────────────────── */
/* Search input + submit button glue together as one pill via Bootstrap's
   input-group, spanning the full width of the dialog it sits in. */
.engagement-search-group {
    width: 100%;
}
.engagement-search-group .form-control {
    border-right: 0;
}
.engagement-search-group .btn {
    border-radius: 0;
}
.engagement-search-group > :last-child {
    border-top-right-radius: 3px;
    border-bottom-right-radius: 3px;
}
.list-per-page-select {
    width: auto;
    min-width: 5rem;
}
.btn.disabled,
.btn-outline-secondary.disabled,
.btn:disabled {
    opacity: 0.35;
    pointer-events: none;
    cursor: default;
}

/* ── API error box: fixed, discardable, shown on HTMX non-2xx responses ──── */
.api-error-box {
    position: fixed;
    top: 1rem;
    right: 1rem;
    max-width: 420px;
    background: var(--blood);
    color: var(--ink);
    border: 1px solid #ff4444;
    border-radius: 4px;
    padding: 0.75rem 1rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.4);
    z-index: 1080;
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.api-error-box-body {
    flex: 1;
}

.api-error-box-close {
    background: transparent;
    border: none;
    color: var(--ink);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    padding: 0;
    opacity: 0.8;
}

.api-error-box-close:hover {
    opacity: 1;
}

/* ── Channels ──────────────────────────────────────────────────────────────── */
/* The section's own rail, under the two every page carries. It takes the
   burgundy a card settles to under the pointer (this application's other
   ground, and the one thing here that is neither the void the archive is drawn
   on nor the slate a record sits on) so being inside Channels is read off the
   page rather than off the label that is underlined.

   The edge that ground comes with is carried too, as a hairline along the
   bottom, because a band of colour with no edge reads as a gap in the page
   rather than as a bar on it. */
.channel-subnav {
    --bar-ground: var(--card-ground-lift);

    border-bottom: 1px solid var(--card-edge-lift);
}

/* On that ground the steel every other rail speaks in has nothing to sit
   against, so the type comes up to the ink the burgundy card already forces
   its own contents to. */
.channel-subnav-link {
    color: rgba(247, 249, 251, 0.72);
}

.channel-subnav-link:hover {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 rgba(247, 249, 251, 0.45);
}

.channel-subnav-link.is-current {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 var(--ink);
}

.channel-subnav-link:focus-visible {
    outline: 2px solid var(--ink);
    outline-offset: -2px;
}

/* On the single channel view, the set's own title card, above the picture the
   way the strip's "On now" and "Then" sit below it: a band seamed to the frame
   by the hairline this application divides everything with, so the two bracket
   the picture as one set. Read in the strip's own voice: the mark in the
   micro-caps this application names a reading with, the channel's name at the
   record step the strip titles a programme at, and what its maker says it is as
   prose beneath it. */
.channel-marquee {
    padding: 0.85rem 1.1rem 0.95rem;
    border-bottom: 1px solid var(--line);
}

.channel-marquee-eyebrow {
    display: block;
    color: var(--gold);
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

.channel-marquee-name {
    display: flex;
    align-items: baseline;
    gap: 0.55rem;
    margin: 0.2rem 0 0;
    color: var(--ink);
    font-size: var(--record-name);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.2;
}

/* The channel's number, ahead of its name: what a viewer knows the channel
   by, so it takes the gold and a step up in size over the name beside it.
   Tabular, so it holds its width as channels are picked. */
.channel-marquee-number {
    color: var(--gold);
    font-size: 1.7rem;
    font-variant-numeric: tabular-nums;
}

.channel-marquee-number:empty {
    display: none;
}

/* What its maker says it is, under the name. Set as the prose it is, to a
   measure so it reads as a sentence rather than running the width of the page. */
.channel-marquee-description {
    margin: 0.4rem 0 0;
    max-width: var(--card-prose-measure);
    color: rgba(247, 249, 251, 0.72);
    font-size: var(--record-prose);
    line-height: var(--card-prose-leading);
}

/* The stage. Before a channel is picked it is the console's own ground with the
   card mounted on it, the same thing the player shows while nothing is
   playing. Tuned to a channel it goes black and the picture owns it, which is
   the same difference the player marks between choosing and watching.

   A channel cannot be wound back, so what the bar carries is what a live
   picture can be told: how loud it is and whether it fills the screen. It stays
   put rather than withdrawing, because there is no transport under it to get in
   the way of the film. */
.channel-stage {
    margin: 0 -1rem 1.25rem;
    transition: background 0.2s ease;
}

.channel-stage.is-tuned {
    background: #000;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
}

.channel-screen {
    position: relative;
    width: 100%;
    margin: 0 auto;
}

/* Tuned, the screen takes the shape a picture has and the room the window
   leaves once the masthead, the rails and the readout above it have had
   theirs. */
.channel-stage.is-tuned .channel-screen {
    aspect-ratio: 16 / 9;
    min-height: 3.4rem;
    max-height: min(72vh, calc(100vh - 15.5rem));
    overflow: hidden;
}

.channel-video {
    display: none;
    width: 100%;
    height: 100%;
    background: #000;
    object-fit: contain;
}

.channel-stage.is-tuned .channel-video {
    display: block;
}

/* Drawn to the edges of the frame, with whatever will not fit carried off the
   sides or the top and bottom. */
.channel-video.is-filling {
    object-fit: cover;
}

/* The bar belongs to a picture, so it is drawn only where there is one. */
.channel-stage:not(.is-tuned) .channel-controls {
    display: none;
}

.channel-routing,
.watch-routing {
    position: absolute;
    top: 0.6rem;
    left: 0.75rem;
    z-index: 5;
    color: var(--fault-edge);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.9);
    pointer-events: none;
}

.channel-routing[hidden],
.watch-routing[hidden] {
    display: none;
}

/* Written twice for the reason the player's full-screen rule is: a selector
   list carrying a pseudo the browser has never heard of is a list it throws
   away entire. */
.channel-screen:fullscreen {
    height: 100vh;
    max-height: none;
    aspect-ratio: auto;
}

.channel-screen:-webkit-full-screen {
    height: 100vh;
    max-height: none;
    aspect-ratio: auto;
}

/* What is on, marked the way this application marks a reading in force. It is
   the one thing on a channel's bar that says what kind of picture this is: not
   a file being played through, but a schedule the clock is inside. */
.channel-live {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0 0.3rem;
    font-size: var(--card-mark);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--gold);
    white-space: nowrap;
}

.channel-live .fas {
    font-size: 0.4rem;
}

/* Off air, the mark goes quiet rather than away: the channel is still the one
   being watched, and it is the state that has changed. */
.channel-stage.is-between .channel-live {
    color: var(--steel);
}

/* ── The test card ─────────────────────────────────────────────────────────── */
/* What a tuned channel shows when nothing is going out: the gaps a half-hour
   schedule leaves between programmes, and a channel with nothing scheduled at
   all. A channel somebody is watching is never a blank screen; it is a card
   and a time to come back at. */
.channel-testcard {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #000;
}

.channel-testcard[hidden] {
    display: none;
}

/* The card fills the frame. It is the channel's own screen rather than
   something shown on it, so a band of black around it would read as a picture
   sitting inside the set, and the card is a flat field of bars and blocks,
   which loses nothing at the edges. */
.channel-testcard-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* What the card says, over it. It takes the ground the player gathers behind
   anything it says over a picture, so it reads against whatever is underneath:
   the card, or a piece of era filler playing in the card's place while the
   channel waits. */
.channel-testcard-say {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 3.2rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    padding: 0.5rem 1rem;
    text-align: center;
    background: var(--nudge-ground);
}

.channel-testcard-say[hidden] {
    display: none;
}

/* What the channel is doing about the gap: looking for something of the era to
   put on, and what it found or why it found nothing. Under the clock, and set
   to be read from where a television is read from: it is the answer to the
   question somebody staring at a test card is asking, and a line they have to
   lean in for is a line that may as well not be there. It is absent altogether
   on a channel that was never asked to fill a gap. */
.channel-testcard-junction {
    font-size: clamp(0.7rem, 1.9vw, 0.95rem);
    font-weight: 600;
    line-height: 1.35;
    letter-spacing: 0.06em;
    color: var(--ink);
}

.channel-testcard-junction[hidden] {
    display: none;
}

/* When the channel is back. */
.channel-testcard-note {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: clamp(0.72rem, 2vw, 1rem);
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ink);
}

/* How long that is from now, counted down under it. In the accent this
   application marks anything live with, and a step under the statement it
   answers; the time is what somebody plans by and this is what they watch.
   Tabular, so the figure holds still as it runs. */
.channel-testcard-clock {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: clamp(0.66rem, 1.7vw, 0.85rem);
    font-weight: 600;
    letter-spacing: 0.12em;
    color: var(--gold);
}

/* A channel with nothing to come back to has nothing to count. */
.channel-testcard-clock:empty {
    display: none;
}

/* ── The player's own menu ─────────────────────────────────────────────────── */
/* What a right click on the picture opens. Drawn on the same slate and hairline
   every menu in this application is, and holding what a channel actually has to
   give rather than what a browser assumes a picture does. */
.channel-menu {
    position: fixed;
    z-index: 1090;
    min-width: 11rem;
    padding: 0.35rem;
    background: var(--slate);
    border: 1px solid var(--line);
    border-left: 3px solid var(--gold);
    border-radius: 3px;
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.7);
}

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

.channel-menu-action {
    display: block;
    width: 100%;
    padding: 0.35rem 0.5rem;
    border: 0;
    border-radius: 2px;
    background: none;
    text-align: left;
    color: var(--steel);
    font-family: inherit;
    font-size: var(--card-control);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.12s ease, color 0.12s ease;
}

.channel-menu-action:hover,
.channel-menu-action:focus-visible {
    background: var(--void);
    color: var(--gold);
    text-decoration: none;
}

.channel-menu-action:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
}

@media (prefers-reduced-motion: reduce) {
    .channel-menu-action {
        transition: none;
    }
}

.channel-menu-audio {
    margin-top: 0.25rem;
    padding-top: 0.25rem;
    border-top: 1px solid var(--line);
}

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

.channel-menu-label {
    display: block;
    padding: 0.2rem 0.5rem;
    color: var(--steel);
    font-size: var(--card-control);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.7;
}

.channel-menu-action.is-on {
    color: var(--gold);
}

/* ── The channel selector ──────────────────────────────────────────────────── */
/* Every channel there is, listed the way a set lists them: one line each, led
   by the channel's number, then its name, what is on it, and the hour that
   runs to.

   The number is what a viewer knows a channel by, so it leads the line and is
   set large and light in the tabular figures this application gives every
   figure, making a column of numbers down the left that is read before
   anything else on the line. Under the programme runs how much of it has gone,
   which is the reading a name cannot give: joining a film with eleven minutes
   left is not the offer the title makes it look. The hours make a column down
   the right, so what is nearly over and what has just started are told apart
   without reading a word.

   It is part of the set. It takes the stage's own bleed and the hairlines the
   stage is seamed with, and no box of its own: the frame, the strip under it
   and this are one instrument. Each line is set in the strip's own voice: the
   number in the face the marquee names a channel with, the programme in the
   strip's ink, the hour in the strip's gold. The channel being watched is the
   one line lit. */
.channel-rail {
    margin: 0 -1rem;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    background: rgba(0, 0, 0, 0.35);
}

/* The row that opens and closes the list. It arrives closed, and the row is
   the whole of what is seen of the list then: what it is, how many channels
   it holds, and the chevron that says it opens. Set in the strip's voice, and
   as wide as the list it opens. */
.channel-rail-toggle {
    display: flex;
    align-items: baseline;
    gap: 0.7rem;
    width: 100%;
    padding: 0.85rem 1.1rem;
    border: 0;
    background: none;
    color: var(--ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: background 0.12s ease;
}

.channel-rail-toggle:hover {
    background: rgba(247, 249, 251, 0.04);
}

.channel-rail-toggle:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
}

/* Open, the row is seamed to the list beneath it by the hairline everything
   here is divided by. */
.channel-rail-toggle[aria-expanded="true"] {
    border-bottom: 1px solid var(--line);
}

.channel-rail-title {
    font-size: var(--card-name);
    font-weight: 600;
    letter-spacing: -0.01em;
}

.channel-rail-count {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    color: var(--steel);
}

/* The chevron at the end of the row, turned over while the list is open, so
   the row says which way it will go. */
.channel-rail-chevron {
    margin-left: auto;
    align-self: center;
    color: var(--steel);
    font-size: var(--card-fact);
    transition: transform 0.16s ease, color 0.12s ease;
}

.channel-rail-toggle:hover .channel-rail-chevron {
    color: var(--gold);
}

.channel-rail-toggle[aria-expanded="true"] .channel-rail-chevron {
    transform: rotate(180deg);
}

.channel-rail-body[hidden] {
    display: none;
}

a.channel-item {
    display: grid;
    grid-template-columns: 3.4rem 5.2rem minmax(0, 1fr) auto;
    align-items: center;
    gap: 0 1.2rem;
    padding: 0.85rem 1.1rem;
    border-bottom: 1px solid var(--line);
    color: inherit;
    text-decoration: none;
    transition: background 0.12s ease, box-shadow 0.12s ease;
}

.channel-rail-body a.channel-item:last-child {
    border-bottom: 0;
}

/* Under the pointer the line comes up a shade, the way the picture's own menu
   answers the pointer, and the mark rides inside it rather than on its edge so
   nothing in the four columns moves when a channel comes on. */
a.channel-item:hover,
a.channel-item:focus {
    color: inherit;
    text-decoration: none;
    background: rgba(247, 249, 251, 0.04);
}

a.channel-item:hover .channel-item-number,
a.channel-item:focus .channel-item-number {
    color: var(--ink);
}

a.channel-item:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
}

/* The channel being watched, marked the way this application marks a reading
   in force everywhere else: the gold rule at its edge, and its number and hour
   in the gold the strip above already speaks in. */
.channel-item.is-playing {
    background: rgba(201, 166, 52, 0.06);
    box-shadow: inset 3px 0 0 var(--gold);
}

.channel-item.is-playing .channel-item-number,
.channel-item.is-playing .channel-item-when {
    color: var(--gold);
}

/* The channel's fixed number, in the face and at the step the marquee names a
   channel with, so the column of them reads as the set's own numbers. Tabular,
   so they stand under each other. */
.channel-item-number {
    justify-self: end;
    font-variant-numeric: tabular-nums;
    font-size: var(--record-name);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--steel);
    transition: color 0.12s ease;
}

/* The channel, and what is on it, stacked as the strip stacks a programme's
   title over its second line. */
.channel-item-body {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}

.channel-item-name {
    font-size: var(--record-second);
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.3;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* What is on it, in the ink the strip gives a programme's second line, held to
   one line: the lines of a list are the same height, and a title of nine words
   would make one of them two. */
.channel-item-on {
    font-size: var(--card-second);
    line-height: 1.35;
    color: rgba(247, 249, 251, 0.72);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A channel between programmes, or one with nothing scheduled at all. */
.channel-item-on.is-off {
    color: var(--steel);
}

/* The hour, in the column the hours make, in the mono the strip writes every
   clock in. Tabular, so the figures stand under each other down the list rather
   than shifting with the digits. */
.channel-item-when {
    justify-self: end;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    line-height: 1.35;
    color: var(--steel);
    white-space: nowrap;
}

/* A programme that is on has its hour in the gold the strip gives the clock of
   what is on; a channel off air keeps the label colour, as the strip's "Then"
   does. */
.channel-item-when.is-on {
    color: var(--gold);
}

.channel-item-when:empty {
    display: none;
}

/* Narrow enough that four columns leave none of them readable. The number
   keeps its place at the head of the line, the still goes the way the strip's
   own goes on a phone, and the rest stacks in the order it is read in. */
@media (max-width: 767px) {
    a.channel-item {
        grid-template-columns: 2.6rem minmax(0, 1fr);
        gap: 0.25rem 1rem;
        padding: 0.8rem 0.9rem;
    }

    .channel-item-number {
        grid-row: span 2;
        align-self: start;
        font-size: 1.1rem;
    }

    .channel-item-when {
        grid-column: 2;
        justify-self: start;
    }
}

@media (prefers-reduced-motion: reduce) {
    a.channel-item,
    .channel-item-number,
    .channel-rail-toggle,
    .channel-rail-chevron {
        transition: none;
    }
}

/* ── Making a channel ──────────────────────────────────────────────────────── */
/* The form is a dialog: making a channel and changing one are the same
   questions, and a page carrying the answers to them permanently would be a
   page that is mostly a form. */
.channel-form-field {
    margin-bottom: 1.1rem;
}

.channel-form-field:last-child {
    margin-bottom: 0;
}

/* What a field is called, above the control that answers it. Held to the
   field's own label; a switch is a label too, and reaching it would lay the
   slider out under its own wording in micro-caps. */
.channel-form-field > label,
.channel-form-label {
    display: block;
    margin-bottom: 0.35rem;
    color: var(--steel);
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.channel-form-note {
    display: block;
    margin-top: 0.35rem;
    color: var(--steel);
    font-size: var(--card-fact);
}

/* The genres, as the tags they are on every other page here. The control is a
   checkbox so the set chosen is submitted by the form itself and the page needs
   nothing running to hold it; what is drawn is the tag. */
.channel-form-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem 0.45rem;
}

.channel-form-tag input,
.channel-form-mode input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.channel-form-tag {
    position: relative;
    margin: 0;
    cursor: pointer;
}

.channel-form-tag-mark {
    display: inline-block;
    padding: 0.18rem 0.5rem;
    border: 1px solid var(--line);
    border-radius: 2px;
    color: var(--steel);
    font-size: var(--card-fact);
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}

.channel-form-tag:hover .channel-form-tag-mark {
    color: var(--ink);
    border-color: var(--card-edge-lift);
}

.channel-form-tag input:checked + .channel-form-tag-mark {
    background: var(--gold);
    border-color: var(--gold);
    color: var(--void);
}

.channel-form-tag input:focus-visible + .channel-form-tag-mark {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* How the mix is read. Two answers to one question, so they are one control
   with two states rather than two controls. */
.channel-form-modes {
    display: inline-flex;
    border: 1px solid var(--line);
    border-radius: 2px;
    overflow: hidden;
}

.channel-form-mode {
    position: relative;
    margin: 0;
    cursor: pointer;
}

.channel-form-mode + .channel-form-mode .channel-form-mode-mark {
    border-left: 1px solid var(--line);
}

.channel-form-mode-mark {
    display: inline-block;
    padding: 0.3rem 0.9rem;
    color: var(--steel);
    font-size: var(--card-mark);
    font-weight: 600;
    letter-spacing: 0.14em;
    transition: color 0.12s ease, background 0.12s ease;
}

.channel-form-mode:hover .channel-form-mode-mark {
    color: var(--ink);
}

.channel-form-mode input:checked + .channel-form-mode-mark {
    background: var(--gold);
    color: var(--void);
}

.channel-form-mode input:focus-visible + .channel-form-mode-mark {
    outline: 2px solid var(--gold);
    outline-offset: -2px;
}

/* A genre the channel cannot be made of (one neither library it draws from is
   filed under) is off the line rather than greyed on it: it is not a choice
   being withheld, it is a tag that would select nothing. */
.channel-form-tag[hidden] {
    display: none;
}

.channel-form-tags[hidden],
.channel-form-note[hidden] {
    display: none;
}

.channel-criteria-add-button {
    padding: 0;
    border: 0;
    background: none;
    color: var(--gold);
    font: inherit;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    cursor: pointer;
}

.channel-criteria-add-button:hover {
    text-decoration: underline;
}

.channel-criteria-add-button:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
}

/* One channel that has been made, on the row language every listing here
   reads down. */
.channel-row {
    display: flex;
    align-items: flex-start;
    gap: 0.9rem;
    padding: 0.7rem 1.1rem;
    background: var(--card-ground);
    border: 1px solid var(--line);
    border-left: 3px solid transparent;
    border-radius: 3px;
    margin-bottom: 0.4rem;
    transition: border-color 0.12s ease, background 0.12s ease;
}

.channel-row:hover {
    border-top-color: var(--card-edge-lift);
    border-right-color: var(--card-edge-lift);
    border-bottom-color: var(--card-edge-lift);
    border-left-color: var(--gold);
    background: var(--card-ground-lift);
}

.channel-row-mark {
    flex: 0 0 auto;
    color: var(--steel);
    font-size: var(--card-figure);
    line-height: 1.4;
}

.channel-row-body {
    flex: 1 1 auto;
    min-width: 0;
}

.channel-row-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
}

.channel-row-title {
    color: var(--ink);
    font-size: var(--card-name);
    font-weight: 600;
}

/* What the channel is a reading of, under what it is called. */
.channel-row-reading {
    display: block;
    margin-top: 0.1rem;
    color: var(--steel);
    font-size: var(--card-second);
}

.channel-row-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.6rem;
    margin-top: 0.15rem;
}

.channel-row-fact,
.channel-row-who,
.channel-row-when {
    color: var(--steel);
    font-size: var(--card-fact);
}

/* What the channel draws on, counted, in the tabular figures every count here
   is set in. A channel nothing answers says so in the fault colour: it is a
   channel that will put on nothing. */
.channel-row-pool {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: var(--card-fact);
    color: var(--ink);
}

.channel-row-pool.is-empty {
    color: var(--fault-edge);
}

/* What can be done with the channel, stacked at the end of the row the way
   every other card in this application stacks its controls. */
.channel-row-controls {
    flex: 0 0 auto;
    align-self: flex-start;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.25rem;
    min-width: 9.5rem;
}

/* Inside the column each control is a full-width row of it, so the labels line
   up down the left edge rather than each button being its own width. */
.channel-row-controls .btn {
    margin: 0 !important;
    width: 100%;
    text-align: left;
    font-size: var(--card-control);
    padding: var(--card-control-pad);
    letter-spacing: 0.04em;
    white-space: nowrap;
}

/* What the channel is showing this minute, under its name. */
.guide-channel-now {
    color: var(--gold);
    font-size: var(--card-mark);
    letter-spacing: 0.04em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.guide-channel-now.is-off {
    color: var(--steel);
}

.guide-lane-empty {
    flex: 1 1 auto;
    margin: 0;
    padding: 0.9rem 0.75rem;
    color: var(--steel);
    font-size: var(--card-fact);
}

/* ── News ──────────────────────────────────────────────────────────────────── */
/* The section's own rail, on the slate a record sits on, so being inside News
   is read off the page the way Channels is read off its burgundy. */
.news-subnav {
    --bar-ground: var(--slate);
}

.news-subnav-link {
    color: rgba(247, 249, 251, 0.72);
}

.news-subnav-link:hover {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 rgba(247, 249, 251, 0.45);
}

.news-subnav-link.is-current {
    color: var(--gold);
    box-shadow: inset 0 -2px 0 var(--gold);
}

/* ── On Demand ─────────────────────────────────────────────────────────────── */
/* The section's own rail, on the blue the page's gradient opens with, so being
   inside On Demand is read off the page the way News is read off its slate. */
.on-demand-subnav {
    --bar-ground: #002a45;
}

.on-demand-subnav-link {
    color: rgba(247, 249, 251, 0.72);
}

.on-demand-subnav-link:hover {
    color: var(--ink);
    box-shadow: inset 0 -2px 0 rgba(247, 249, 251, 0.45);
}

.on-demand-subnav-link.is-current {
    color: var(--gold);
    box-shadow: inset 0 -2px 0 var(--gold);
}

/* The criteria on the Configure page: three lines of tags in one card, the
   tags the ones every other page here chooses with. */
.news-criteria-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 0.4rem;
}

.library-section-heading {
    margin: 1.4rem 0 0.6rem;
    color: var(--steel);
    font-size: var(--card-eyebrow);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

/* What the search found, headed by its count and the control that puts the
   whole of it on. */
.news-found-heading {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.news-found-heading .library-section-heading {
    margin-bottom: 0;
}

/* How many requests are waiting, set beside the reading that opens them in
   the figure face the readouts use. */
.library-sort-count {
    margin-left: 0.35rem;
    font-family: var(--font-mono, monospace);
    font-size: 0.78rem;
    color: var(--gold);
}

/* ── A title, as the public sees it ──────────────────────────────────────── */
/* The page a shared link to a film or a series opens on for somebody with no
   session: a one-sheet. The poster stands lit on the left, and its own colour
   is spread and softened across the card behind it, so every title arrives in
   a light of its own. The name is set at the largest size anywhere in this
   application, the figures IMDb publishes run beneath it, and the billing runs
   along the foot of the card the way it runs along the foot of a theatrical
   poster. The film and the series share the drawing; the series simply has
   more figures to put in it. */
.preview-sheet {
    position: relative;
    margin: 0.5rem 0 2rem;
    border: 1px solid rgba(201, 166, 52, 0.22);
    border-radius: 6px;
    background: rgba(0, 14, 25, 0.78);
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
}

.preview-hero {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 17rem) minmax(0, 1fr);
    gap: 2.6rem;
    align-items: start;
    padding: 2.6rem 2.6rem 2.2rem;
    isolation: isolate;
}

/* The layer the poster's light is drawn in. It clips the light to the top of
   the card on its own, which leaves the card itself unclipped, so the menu of
   controls a signed-in reader opens can hang past its edge. */
.preview-hero-glow {
    position: absolute;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    border-radius: 6px 6px 0 0;
    pointer-events: none;
}

/* The poster's light. Blown up past the card's edges and blurred until only
   its colour is left, then sunk into the ground towards the foot and the right
   of the card so the type stands on something steady. */
.preview-hero-light {
    position: absolute;
    top: -20%;
    left: -10%;
    width: 120%;
    height: 140%;
    object-fit: cover;
    filter: blur(56px) saturate(1.35);
    opacity: 0.38;
    animation: preview-light-up 1.6s ease-out both;
}

.preview-hero-glow::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        linear-gradient(90deg, rgba(0, 14, 25, 0) 0%, rgba(0, 14, 25, 0.55) 60%, rgba(0, 14, 25, 0.7) 100%),
        linear-gradient(180deg, rgba(0, 14, 25, 0) 55%, rgba(0, 14, 25, 0.9) 100%);
}

/* The one movement on the page: the light coming up behind the poster as the
   page opens, the way a house darkens and the screen brightens. */
@keyframes preview-light-up {
    from { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .preview-hero-light {
        animation: none;
    }
}

.preview-poster-zoom {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: zoom-in;
    border-radius: 3px;
}

.preview-poster-zoom:focus-visible {
    outline: 2px solid var(--gold);
    outline-offset: 4px;
}

.preview-poster-image,
.preview-poster-empty {
    display: block;
    width: 100%;
    border-radius: 3px;
    box-shadow: 0 0 0 1px rgba(201, 166, 52, 0.45),
                0 24px 60px rgba(0, 0, 0, 0.7);
}

.preview-poster-image {
    height: auto;
    background: var(--void);
}

.preview-poster-empty {
    aspect-ratio: 2 / 3;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--void);
    color: var(--steel);
    font-size: 3rem;
}

.preview-body {
    min-width: 0;
    padding-top: 0.4rem;
}

/* The name, at the size the room is read from. Tight, heavy and close-set, so
   a long title holds together as one shape rather than a line of words. */
.preview-title {
    margin: 0;
    font-size: clamp(2rem, 4.6vw, 3.6rem);
    font-weight: 700;
    line-height: 1.02;
    letter-spacing: -0.035em;
    color: var(--ink);
    text-wrap: balance;
    overflow-wrap: anywhere;
}

/* The figures, each standing over what it measures, divided by the gold
   hairline everything that is a reading is drawn in. The list is drawn out to
   the left by one gutter and clipped there, so the hairline in front of
   whichever figure opens a line falls outside it and every line starts flush
   with the title. */
.preview-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 1.1rem 0;
    margin: 1.6rem 0 0 -1.4rem;
    overflow: hidden;
}

.preview-fact {
    display: flex;
    flex-direction: column-reverse;
    gap: 0.3rem;
    margin-left: -1px;
    padding: 0 1.4rem;
    border-left: 1px solid rgba(201, 166, 52, 0.35);
}

.preview-fact-figure {
    margin: 0;
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
    white-space: nowrap;
}

.preview-fact-unit {
    margin-left: 0.1rem;
    font-size: 0.85rem;
    font-weight: 500;
    letter-spacing: 0;
    color: rgba(247, 249, 251, 0.55);
}

.preview-fact-label {
    font-size: 0.74rem;
    font-weight: 500;
    line-height: 1.3;
    color: rgba(247, 249, 251, 0.6);
}

/* Metacritic's score in the colours Metacritic itself reads it by: favourable,
   mixed, unfavourable. */
.preview-metascore {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.1rem;
    padding: 0.2rem 0.35rem;
    border-radius: 3px;
    font-size: 1.15rem;
    color: #0a0a0a;
    background: var(--steel);
}

.preview-metascore.is-favourable {
    background: #66cc33;
}

.preview-metascore.is-mixed {
    background: #ffcc33;
}

.preview-metascore.is-unfavourable {
    background: #ff4d4d;
}

.preview-genres {
    margin: 1.5rem 0 0;
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--gold);
}

/* For a signed-in reader each genre is the listing filtered to it, and each
   name the listing searched for them. They read as the words they are until
   the pointer is on one. */
.preview-genres a,
.preview-billing-name a {
    color: inherit;
    text-decoration: none;
}

.preview-genres a:hover,
.preview-genres a:focus-visible,
.preview-billing-name a:hover,
.preview-billing-name a:focus-visible {
    color: var(--gold);
    text-decoration: none;
    box-shadow: inset 0 -1px 0 currentColor;
    outline: none;
}

.preview-genres a:hover,
.preview-genres a:focus-visible {
    color: var(--ink);
}

.preview-synopsis {
    max-width: 62ch;
    margin: 0.7rem 0 0;
    font-size: 1.05rem;
    line-height: 1.6;
    color: rgba(247, 249, 251, 0.86);
}

.preview-imdb {
    display: inline-block;
    margin-top: 1.3rem;
    font-size: 0.82rem;
    font-weight: 500;
    color: rgba(247, 249, 251, 0.6);
    text-decoration: none;
    border-bottom: 1px solid rgba(201, 166, 52, 0.4);
}

.preview-imdb:hover,
.preview-imdb:focus-visible {
    color: var(--gold);
    border-bottom-color: var(--gold);
    text-decoration: none;
    outline: none;
}

/* The way back to the library a title page was opened from, set quietly above
   the sheet. */
.preview-trail {
    display: inline-block;
    margin: 0.2rem 0 0.6rem;
    font-size: 0.8rem;
    font-weight: 500;
    color: rgba(247, 249, 251, 0.6);
    text-decoration: none;
}

.preview-trail:hover,
.preview-trail:focus-visible {
    color: var(--gold);
    text-decoration: none;
}

/* For a signed-in reader: the flags the library card carries for whatever is
   wrong with the title, a note on the sheet rather than part of the title. */
.preview-archive {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.35rem 0.6rem;
    margin-top: 1.4rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
    font-size: 0.8rem;
}

.preview-archive-flags {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin: 0;
}

/* The library card's controls, laid along one line under the synopsis rather
   than stacked down the card's edge: playing it leads in gold, queueing it
   follows, and the menu holding the rest closes the line. */
.preview-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1.1rem;
}

/* Set a step above the card's own control size, the three of them alike,
   which the gold control's own sizing would otherwise hold back. */
.preview-controls > .btn,
.preview-controls > .library-card-actions > .btn {
    padding: 0.45rem 1.1rem !important;
    font-size: 0.82rem !important;
    letter-spacing: 0.02em;
}

/* The billing block. Each credit is named small and light, and the people
   under it run on heavy and close, centred across the card in one block the
   way the foot of a one-sheet carries its credits. A name never breaks across
   a line. */
.preview-billing {
    margin: 0;
    padding: 1.9rem 2.6rem;
    border-top: 1px solid rgba(201, 166, 52, 0.22);
    text-align: center;
    line-height: 2;
    text-wrap: balance;
}

.preview-billing-credit {
    margin: 0 0.9rem;
}

/* Set inside the first name it bills, so a credit never ends one line with the
   people it names starting the next. */
.preview-billing-role {
    margin-right: 0.55rem;
    font-size: 0.72rem;
    font-weight: 400;
    color: rgba(247, 249, 251, 0.55);
}

.preview-billing-name {
    display: inline-block;
    margin: 0 0.4rem;
    font-size: 0.95rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--ink);
    white-space: nowrap;
}

/* The rest of what IMDb records, one line to a subject: what it is about on
   the left, the reading on the right, ruled off from the next. */
.preview-ledger {
    margin: 0;
    padding: 0.4rem 2.6rem 1.6rem;
    border-top: 1px solid rgba(201, 166, 52, 0.22);
}

.preview-ledger-row {
    display: grid;
    grid-template-columns: minmax(9rem, 14rem) minmax(0, 1fr);
    gap: 1.5rem;
    padding: 0.85rem 0;
    border-bottom: 1px solid var(--line);
}

.preview-ledger-row:last-child {
    border-bottom: 0;
}

.preview-ledger-label {
    font-size: 0.8rem;
    font-weight: 500;
    color: rgba(247, 249, 251, 0.6);
}

.preview-ledger-value {
    margin: 0;
    max-width: 78ch;
    font-size: 0.92rem;
    line-height: 1.55;
    color: var(--ink);
}

/* The companies run to dozens on a well-travelled title, every distributor of
   every print, so they are set a step smaller to read as the list they are. */
.preview-ledger-value-long {
    font-size: 0.82rem;
    color: rgba(247, 249, 251, 0.78);
}

/* A company that opens the titles it is credited on, reading as the name it
   is until the pointer is on it. */
.preview-ledger-link {
    color: inherit;
    text-decoration: none;
}

.preview-ledger-link:hover,
.preview-ledger-link:focus-visible {
    color: var(--gold);
    text-decoration: none;
    box-shadow: inset 0 -1px 0 currentColor;
    outline: none;
}

@media (max-width: 767px) {
    .preview-hero {
        grid-template-columns: minmax(0, 1fr);
        gap: 1.6rem;
        padding: 1.4rem 1rem 1.6rem;
    }

    .preview-poster {
        width: 10rem;
    }

    .preview-facts {
        margin-left: -1rem;
    }

    .preview-fact {
        padding: 0 1rem;
    }

    .preview-fact-figure {
        font-size: 1.25rem;
    }

    .preview-billing {
        padding: 1.4rem 1rem;
    }

    .preview-billing-credit {
        display: block;
        margin: 0;
    }

    .preview-ledger {
        padding: 0.2rem 1rem 1.2rem;
    }

    .preview-ledger-row {
        grid-template-columns: minmax(0, 1fr);
        gap: 0.25rem;
    }
}

