/*
 * Telestrator - the public site's visual system.
 *
 * The world is a fight broadcast's own graphics package: keyed plates with
 * hard edges over a video-black field, colours that mean exactly one thing
 * each, and a yellow that belongs to the analyst's marker and nothing else.
 *
 * Rules that hold this together, and which are easy to break by accident:
 *
 * - **No glow.** Broadcast keys are flat and opaque. A coloured halo, a
 *   gradient card or a blurred edge turns this into the neon dashboard every
 *   other MMA site already is.
 * - **Violet and cyan are the two sides of one bout, and nothing more.**
 *   They say which lane of a two-sided graphic belongs to whom. They are not
 *   the corners - the corpus holds no corner assignment - and they never
 *   carry the result, which is stated in words. Every side surface is paired
 *   with the fighter's name, because colour alone cannot carry it.
 * - **Red and blue are the finish family**, KO/TKO and submission. That is
 *   all they have ever meant on the career strip, and now it is all they
 *   mean anywhere.
 * - **Yellow is the telestrator.** It marks what the analyst circled - a
 *   maximum, a decisive round. Never a border, never a heading, never a link.
 * - **Green means absent.** Chroma key green is the colour of something
 *   removed from the picture. Here it means the database does not hold this,
 *   and it is the only thing it ever means.
 */

/* ---------------------------------------------------------------- tokens */

:root {
    /* Field. A video black that is cool rather than neutral, so the
       saturated plates sit on it as light rather than as stickers. */
    --field: #0a0e12;
    --field-2: #10151b;
    --field-3: #171e26;
    --field-4: #1f2833;

    --rule: #232c36;
    --rule-bright: #37444f;

    --bone: #edf1f4;
    --bone-2: #a7b4c0;
    --bone-3: #7a8894;

    /* The two fighters in one bout. Side identity only - which lane in a
       two-sided graphic belongs to whom - and deliberately NOT the corners.
       The corpus holds no corner assignment: `fight_participants.corner` is
       the source's column order, and in this dataset the winner is always
       listed first, so it encodes the result and nothing else. Red and blue
       would read as a corner claim to anyone who follows the sport, so the
       pair is drawn from outside the sport's vocabulary. Every side surface
       still carries the fighter's name, so hue is never the only signal. */
    --side-a: #9d6bff;
    --side-b: #22d3ee;

    /* Text that sits on a saturated side plate. Violet takes white; cyan is
       far too light for it and takes the field colour instead. */
    --on-side-a: #ffffff;
    --on-side-b: #08131f;

    /* How a bout ended. Freed from corner duty, red and blue can carry the
       finish family - which the career strip has always used them for. */
    --ko: #e8402a;
    --sub: #3b8ff3;

    /* The analyst's marker. */
    --tele: #ffd400;

    /* Keyed out - the data is not there. */
    --key: #00b140;

    --sans: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --mono: 'Azeret Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;

    /* Type. Display is capped below the 6rem ceiling; the impact here comes
       from width and weight, not from size alone. */
    --t-mega: clamp(2.75rem, 7.5vw, 5.5rem);
    --t-xl: clamp(1.875rem, 3.6vw, 3rem);
    --t-lg: clamp(1.375rem, 2.2vw, 1.875rem);
    --t-md: 1.125rem;
    --t-base: 1rem;
    --t-sm: 0.875rem;
    --t-xs: 0.75rem;
    --t-2xs: 0.6875rem;

    --s-1: 0.25rem;
    --s-2: 0.5rem;
    --s-3: 0.75rem;
    --s-4: 1rem;
    --s-5: 1.5rem;
    --s-6: 2rem;
    --s-7: 3rem;
    --s-8: 4rem;
    --s-9: 6rem;

    /* The safe area. Broadcast graphics never touch the edge of frame. */
    --safe: clamp(1rem, 4vw, 3.5rem);
    --frame: 1440px;

    --cut: 18px; /* the angled edge of a lower third */
}

/* ------------------------------------------------------------- baseline */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--field);
    color: var(--bone);
    font-family: var(--sans);
    font-size: var(--t-base);
    line-height: 1.55;
    font-synthesis: none;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

/*
 * A corner falloff and nothing else.
 *
 * This carried interlace hairlines until they were read as a broken monitor
 * rather than as a screen - which is a fair reading, since a full-page scanline
 * is indistinguishable from a display fault. Texture now comes from things that
 * mean something: the hatched attempt blocks, the crop marks, the SMPTE bars,
 * and the marquee rules.
 *
 * Kept deliberately faint. This layer is fixed to the viewport, so anything
 * heavier permanently dims whatever is at the top or bottom of the screen.
 */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 9999;
    pointer-events: none;
    background: radial-gradient(125% 95% at 50% 42%, transparent 78%, rgba(0, 0, 0, 0.22) 100%);
}

h1,
h2,
h3,
h4,
p,
figure,
ul,
ol {
    margin: 0;
}

ul,
ol {
    padding: 0;
    list-style: none;
}

a {
    color: inherit;
    text-decoration: none;
}

img,
svg {
    display: block;
    max-width: 100%;
}

:focus-visible {
    outline: 2px solid var(--tele);
    outline-offset: 3px;
}

::selection {
    background: var(--tele);
    color: #000;
}

.u-vh {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ----------------------------------------------------------- typography */

/* Names, headings and anything that behaves like a title in the package.
   Archivo's width axis does the work: condensed for names, expanded for the
   numbers, one family holding both ends. */
.tx-name {
    font-family: var(--sans);
    font-stretch: 70%;
    font-weight: 800;
    text-transform: uppercase;
    line-height: 0.92;
    letter-spacing: -0.005em;
}

.tx-title {
    font-family: var(--sans);
    font-stretch: 82%;
    font-weight: 800;
    text-transform: uppercase;
    line-height: 1.02;
    letter-spacing: 0.002em;
}

/* The small tracked capitals a broadcast uses to label everything. */
.tx-label {
    font-family: var(--sans);
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    line-height: 1.2;
    color: var(--bone-3);
}

.tx-label--tele {
    color: var(--tele);
}

/* Every number that can be compared down a column. */
.tx-num {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
    letter-spacing: -0.03em;
}

.tx-read {
    max-width: 68ch;
    color: var(--bone-2);
    font-size: var(--t-md);
    line-height: 1.65;
}

.tx-read strong {
    color: var(--bone);
    font-weight: 600;
}

/* -------------------------------------------------------------- motion */

/*
 * No entrance motion. Furniture used to wipe and rise in on arrival, staggered
 * down the first screen with `animation-delay` up to 460ms and `backwards`
 * fill - which meant the content was held at opacity 0 for up to a second
 * before it was allowed to appear. It cost the reader time on every page load
 * and bought a flourish nobody asked to see twice.
 *
 * Transitions stay. Hover, focus and the round scrubber are responses to
 * something the reader did, which is motion that carries information rather
 * than motion that announces the page.
 */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 1ms !important;
        animation-delay: 0ms !important;
        transition-duration: 1ms !important;
    }
}

/* ------------------------------------------------------------ the frame */

.frame {
    max-width: var(--frame);
    margin: 0 auto;
    padding-inline: var(--safe);
}

.frame--wide {
    max-width: 1680px;
}

/* Safe-area crop marks. Two corners, not four - the mark is a signature,
   not a border. */
.crop {
    position: relative;
}

.crop::before,
.crop::after {
    content: '';
    position: absolute;
    width: 13px;
    height: 13px;
    pointer-events: none;
}

.crop::before {
    top: 0;
    left: 0;
    border-top: 2px solid var(--rule-bright);
    border-left: 2px solid var(--rule-bright);
}

.crop::after {
    right: 0;
    bottom: 0;
    border-right: 2px solid var(--rule-bright);
    border-bottom: 2px solid var(--rule-bright);
}

/* ------------------------------------------------------- master strap */

.strap {
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--field);
    border-bottom: 1px solid var(--rule);
}

.strap__in {
    display: flex;
    align-items: stretch;
    gap: var(--s-5);
    max-width: var(--frame);
    margin: 0 auto;
    padding-inline: var(--safe);
    min-height: 58px;
}

.strap__mark {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-right: var(--s-5);
    border-right: 1px solid var(--rule);
    font-stretch: 70%;
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: 0.01em;
    font-size: var(--t-md);
    white-space: nowrap;
}

.strap__mark svg {
    flex: none;
}

.strap__nav {
    display: flex;
    align-items: center;
    gap: var(--s-5);
}

.strap__nav a {
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--bone-3);
    padding-block: 4px;
    border-bottom: 2px solid transparent;
    transition: color 140ms ease, border-color 140ms ease;
}

.strap__nav a:hover,
.strap__nav a[aria-current] {
    color: var(--bone);
    border-bottom-color: var(--tele);
}

.strap__spacer {
    flex: 1 1 auto;
}

/*
 * Below this width the strap's parts stop fitting on one line, and a flex row
 * that cannot fit sets the page's minimum width - which silently drags every
 * other element off the right edge. The search takes its own row instead.
 */
@media (max-width: 780px) {
    .strap__in {
        flex-wrap: wrap;
        gap: var(--s-3);
        padding-block: var(--s-2);
        min-height: 0;
    }

    .strap__mark {
        border-right: 0;
        padding-right: 0;
    }

    .strap__spacer {
        display: none;
    }

    .strap__nav {
        margin-left: auto;
    }

    .bug-search {
        order: 3;
        flex: 1 0 100%;
    }
}

/* A filter control, built from the search bug so the two read as one kit.
   The native select is reset because its rounded, system-coloured default is
   the one shape this system does not have anywhere: radius is zero and the
   field is video-black. The chevron is drawn rather than left to the platform
   for the same reason. */
.bug-select {
    appearance: none;
    background: var(--field-3);
    border: 1px solid var(--rule-bright);
    border-radius: 0;
    color: var(--bone);
    font-family: var(--sans);
    font-size: var(--t-sm);
    padding: 6px 30px 6px 12px;
    transition: border-color 140ms ease;
    background-image:
        linear-gradient(45deg, transparent 50%, var(--bone-3) 50%),
        linear-gradient(135deg, var(--bone-3) 50%, transparent 50%);
    background-position: right 14px top 52%, right 9px top 52%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
}

.bug-select:focus-visible {
    border-color: var(--tele);
    outline: none;
}

.bug-select option {
    background: var(--field-2);
    color: var(--bone);
}

/* ------------------------------------------------------------- the rack */

/*
 * The operator's rack: the row of labelled controls that narrows a listing.
 * Built from the same plate the search bug is, so the filter strip and the
 * channel bug read as one kit rather than as a form bolted onto a broadcast.
 *
 * `auto-fit` rather than a fixed column count, because the number of controls
 * is not fixed - a fifth one drops in without a breakpoint being touched, and
 * on a phone they stack in one column with no media query at all. The 190px
 * floor is set by the longest option label ("One to three years ago"), which
 * is what a select is sized by.
 */
.rack {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: var(--s-3) var(--s-4);
    margin-top: var(--s-5);
    padding-top: var(--s-4);
    border-top: 1px solid var(--rule);
}

.rack__slot {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    /* Without this a long option name sets the track width and the grid
       overflows the frame rather than wrapping. */
    min-width: 0;
}

.rack__slot .bug-select,
.rack__slot button {
    width: 100%;
}

/* The undo for the filter state, sitting in the strip that states it. Yellow
   is the analyst's mark and never a link, so this takes `bone` and an
   underline - the only two things left that say "clickable" in a strip whose
   other members are not. */
.dateline__clear {
    color: var(--bone);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.dateline__clear:hover {
    color: var(--bone-2);
}

/*
 * What a control cannot see, stated in words. Both notes here are about
 * absence - the third of the roster with no nationality, and the gap between
 * the newest bout on file and today - and neither belongs inside a select as
 * an option, which would read as a group somebody is in.
 *
 * Set as sentences, not in the label voice. The uppercase, letter-spaced label
 * type is for the two or three words that name a control; a caps sentence
 * running to three lines is slower to read than the same sentence in mixed
 * case and louder than a footnote should ever be. They sit in two columns on a
 * wide screen so they balance the rack above rather than stranding it.
 */
.rack__notes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: var(--s-2) var(--s-4);
    margin-top: var(--s-4);
}

.rack__note {
    max-width: 62ch;
    font-size: var(--t-xs);
    line-height: 1.6;
    color: var(--bone-3);
}

/* The search bug docks in the strap, the way a channel bug docks in frame. */
.bug-search {
    display: flex;
    align-items: center;
    align-self: center;
    gap: var(--s-2);
    background: var(--field-3);
    border: 1px solid var(--rule-bright);
    padding: 6px 10px 6px 12px;
    min-width: 180px;
    transition: border-color 140ms ease;
}

.bug-search:focus-within {
    border-color: var(--tele);
}

.bug-search svg {
    flex: none;
    color: var(--bone-3);
}

.bug-search input {
    flex: 1 1 auto;
    min-width: 0;
    background: none;
    border: 0;
    color: var(--bone);
    font-family: var(--sans);
    font-size: var(--t-sm);
    padding: 0;
}

.bug-search input::placeholder {
    color: var(--bone-3);
}

.bug-search input:focus {
    outline: none;
}

/* ------------------------------------------------------------- plates */

.plate {
    background: var(--field-2);
    border: 1px solid var(--rule);
}

.plate--raised {
    background: var(--field-3);
}

/* A lower third: the angled name plate a broadcast puts under a fighter. */
.third {
    position: relative;
    display: block;
    padding: var(--s-3) calc(var(--cut) + var(--s-4)) var(--s-3) var(--s-4);
    clip-path: polygon(0 0, 100% 0, calc(100% - var(--cut)) 100%, 0 100%);
}

.third--dark {
    background: var(--field-4);
    color: var(--bone);
}

/* ---------------------------------------------------------- corner tag */

/* Colour never travels alone: the tag is the label that goes with it. */
.side-tag {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
}

.side-tag::before {
    content: '';
    width: 9px;
    height: 9px;
    flex: none;
}

.side-tag--a {
    color: var(--side-a);
}

.side-tag--a::before {
    background: var(--side-a);
}

.side-tag--b {
    color: var(--side-b);
}

.side-tag--b::before {
    background: var(--side-b);
}

/* --------------------------------------------------------- result strap */

.verdict {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s-2) var(--s-4);
}

.verdict__method {
    font-stretch: 82%;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.01em;
}

.pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    border: 1px solid var(--rule-bright);
    color: var(--bone-2);
    white-space: nowrap;
}

.pill--title {
    border-color: var(--tele);
    color: var(--tele);
}

.pill--win {
    background: var(--bone);
    border-color: var(--bone);
    color: #0a0e12;
}

.pill--loss {
    color: var(--bone-3);
}

/* ------------------------------------------------------- keyed-out data */

/*
 * SMPTE bars. Where a broadcast has no picture it shows the test pattern, and
 * that is exactly the right honesty for a database that does not hold
 * something. This never stands in for a number we do have.
 */
.keyed {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
    border: 1px solid var(--rule);
    background: var(--field-2);
}

.keyed__bars {
    flex: none;
    width: 56px;
    height: 26px;
    background: linear-gradient(
        to right,
        #b4b4b4 0 14.28%,
        #b4b400 14.28% 28.56%,
        #00b4b4 28.56% 42.84%,
        var(--key) 42.84% 57.12%,
        #b400b4 57.12% 71.4%,
        #b40000 71.4% 85.68%,
        #0000b4 85.68% 100%
    );
}

.keyed__text {
    font-size: var(--t-sm);
    color: var(--bone-2);
}

.keyed__text b {
    display: block;
    color: var(--bone);
    font-stretch: 82%;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-size: var(--t-2xs);
}

/* Inline "we don't have this", for a single missing value in a spec row. */
.nodata {
    color: var(--key);
    font-family: var(--mono);
    font-size: 0.85em;
    letter-spacing: -0.02em;
}

/* ------------------------------------------------------------------ flag */

/*
 * A nationality, drawn rather than written. This is a sport whose audience
 * reads a flag at a glance and reads `United Kingdom of Great Britain and
 * Northern Ireland` never - and the roster prints that column fifty times down
 * one page, which is where the written form was worst. The name is not lost:
 * it is the image's alt text, so it is what a screen reader announces and what
 * a search engine indexes, and in the roster the whole row is a link anyway.
 *
 * The files are `webroot/img/flags/{iso_alpha2}.svg`, put there by
 * `npm run flags`; `App\View\Helper\FightHelper::flag()` is the only renderer.
 * The 4:3 box is flag-icons' normalisation, not a claim about any one flag's
 * true ratio, and `object-fit: cover` rather than `contain` because a letterbox
 * inside a bordered plate reads as a rendering fault at 26px across.
 */
.flag {
    display: inline-block;
    box-sizing: border-box;
    width: 26px;
    height: 20px;
    vertical-align: -0.35em;
    object-fit: cover;
    border: 1px solid var(--rule-bright);
    background: var(--field-3);
}

/*
 * Absence at the flag's own size, and it is the reason a flag can replace the
 * written line at all. A country name keys out to a visible `--` wherever it
 * sits; a flag that is simply not emitted is indistinguishable from an image
 * that failed to load. So the slot is drawn, hatched the way an attempted
 * strike is, with the keyed-out marker in it.
 */
.flag--absent,
.flag--named {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.flag--absent {
    background:
        repeating-linear-gradient(135deg, transparent 0 4px, rgba(255, 255, 255, 0.055) 4px 8px),
        var(--field-3);
}

.flag--absent .nodata {
    font-size: var(--t-2xs);
}

/*
 * The repository holds no file for a code the corpus does hold. That is a gap
 * here rather than a gap in the data, and the two must not look alike, so it
 * says the country instead of keying out - which would claim we hold no
 * nationality when we hold one. Nothing in the corpus reaches this today; all
 * 91 codes the roster carries resolve.
 */
.flag--named {
    width: auto;
    padding: 0 5px;
    font-family: var(--sans);
    font-size: var(--t-2xs);
    font-weight: 700;
    line-height: 1.1;
    text-align: center;
    color: var(--bone-2);
}

/* ------------------------------------------------------- round bar pair */

/*
 * The signature graphic: significant strikes landed per round, drawn from a
 * centre rule, red left and blue right. This is the shape the round-stat
 * table is actually in, and no other MMA site draws it because no other one
 * stores the rounds.
 */
.rounds {
    display: grid;
    gap: 1px;
    background: var(--rule);
    border: 1px solid var(--rule);
}

.rnd {
    display: grid;
    grid-template-columns: 1fr 58px 1fr;
    align-items: center;
    background: var(--field-2);
    padding: 10px var(--s-3);
    gap: var(--s-2);
    position: relative;
}

.rnd--peak {
    background: var(--field-3);
}

.rnd__no {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    font-family: var(--mono);
    font-size: var(--t-2xs);
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--bone-3);
    text-align: center;
}

.rnd__side {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    min-width: 0;
}

.rnd__side--a {
    flex-direction: row-reverse;
}

/* The lane the bar is drawn in. The track's width is a percentage, so it needs
   a container of its own - sharing a flex row with the figure made the two
   fight for the same space and pushed the row wider than its column. */
.rnd__lane {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
}

.rnd__side--a .rnd__lane {
    justify-content: flex-end;
}

/* Attempts are the hatched block; landed is the solid one inside it. Accuracy
   is therefore readable as a proportion without a second chart, and a round
   where someone threw a lot and hit nothing looks like what it was. */
.rnd__track {
    display: flex;
    align-items: center;
    flex: none;
    min-width: 3px;
    height: 16px;
    background: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.055) 0 2px,
        transparent 2px 5px
    );
    box-shadow: inset 0 0 0 1px var(--rule);
}

.rnd__track--a {
    justify-content: flex-end;
}

.rnd__bar {
    height: 16px;
    flex: none;
}

.rnd__bar--a {
    background: var(--side-a);
}

.rnd__bar--b {
    background: var(--side-b);
}

/*
 * Landed and thrown, in the same two tones as the bars beside them: the bright
 * figure matches the solid block, the dim one matches the hatched extent. That
 * correspondence is what makes the graphic readable, and it replaced a written
 * legend - a chart that needs a sentence explaining its own fills is a chart
 * that has not finished being designed.
 */
.rnd__val {
    font-family: var(--mono);
    font-size: var(--t-xs);
    font-variant-numeric: tabular-nums;
    color: var(--bone);
    flex: none;
    width: 6.2ch;
    text-align: center;
    white-space: nowrap;
}

.rnd__val i {
    font-style: normal;
    color: var(--bone-3);
}

/* The analyst's mark: a yellow ring drawn round the number of the round that
   decided it - which is the mark a telestrator actually makes. Stretching it
   across the whole row turned it into an ellipse that circled nothing. */
.rnd__mark {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 52px;
    height: 30px;
    margin: -15px 0 0 -26px;
    border: 2px solid var(--tele);
    border-radius: 46% 54% 50% 50% / 56% 44% 56% 44%;
    pointer-events: none;
    opacity: 0.9;
}

/* ---------------------------------------------------------- the opener */

/*
 * The between-rounds full screen: both corners with the round graphic set
 * between them, which is the arrangement a broadcast uses precisely because it
 * makes the fight legible at a glance. On a phone the corners stack and the
 * graphic follows, because a centre rule needs width to mean anything.
 */
.opener {
    display: grid;
    grid-template-areas:
        'a'
        'b'
        'rounds';
    gap: var(--s-5);
}

.opener__corner--a {
    grid-area: a;
}

.opener__corner--b {
    grid-area: b;
}

.opener__rounds {
    grid-area: rounds;
}

@media (min-width: 940px) {
    .opener {
        grid-template-areas: 'a rounds b';
        grid-template-columns: 1fr minmax(300px, 40%) 1fr;
        gap: var(--s-6);
        align-items: start;
    }

    .opener__rounds {
        align-self: center;
    }

    .opener__corner--b {
        text-align: right;
    }
}

/*
 * The name is sized against its own column, not the viewport. At the mega size
 * alone `JASUDAVICIUS` ran 120px out of its column and off the page at a
 * laptop width; a corner is a size container, so a long surname steps down and
 * a short one keeps the full scale.
 */
.opener__corner {
    container-type: inline-size;
    min-width: 0;
}

.opener__name {
    font-size: min(var(--t-mega), 15cqi);
    overflow-wrap: anywhere;
    margin-block: var(--s-3) var(--s-2);
    display: block;
}

/*
 * The rule under a name is what ties that fighter to their lane in the round
 * graphic between the two openers. It replaced a tag reading "Red corner",
 * which named a corner the corpus does not hold; the name above the rule is
 * the label now, so the graphic still needs no written key.
 */
.opener__corner--a .opener__name {
    border-bottom: 4px solid var(--side-a);
}

.opener__corner--b .opener__name {
    border-bottom: 4px solid var(--side-b);
}

.opener__alias {
    color: var(--tele);
    font-stretch: 82%;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-size: var(--t-lg);
}

.opener__rec {
    font-family: var(--mono);
    font-size: var(--t-sm);
    color: var(--bone-2);
    letter-spacing: -0.02em;
    margin-top: var(--s-3);
}

/* The centre column's heading, sitting directly over the round bars. */
.opener__scale {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--s-3);
    margin-bottom: var(--s-2);
}

/*
 * Which bout this is, said under the marquee rather than boxed like it. It was
 * a `.dateline` strip, and with the marquee and the live band above it the
 * first screen was three stacked strips before a single name.
 */
.opener__billing {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--s-2) var(--s-4);
    margin-top: var(--s-3);
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--bone-3);
}

.opener__billing b {
    color: var(--bone);
    font-stretch: 82%;
    letter-spacing: 0.08em;
}

.opener__billing .sep {
    color: var(--rule-bright);
}

/* A bout not yet fought has no rounds to draw, so the centre gives its width
   back to the names and holds only the `vs`. */
@media (min-width: 940px) {
    .opener--booked {
        grid-template-columns: 1fr auto 1fr;
    }
}

.opener__vs {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s-2);
    margin: 0;
    text-align: center;
}

.opener__vs-word {
    font-family: var(--mono);
    font-size: var(--t-lg);
    color: var(--bone-3);
}

.opener__vs-note {
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--bone-3);
}

/* On a live card the opener is the next bout, and this heading is the line
   between the result that just landed above it and the bout still to come. */
.opener__next {
    margin-top: var(--s-7);
    padding-top: var(--s-6);
    border-top: 1px solid var(--rule);
    text-align: center;
    font-stretch: 82%;
    font-weight: 800;
    text-transform: uppercase;
    font-size: var(--t-lg);
    letter-spacing: 0.01em;
}

/*
 * The live band as the front page's headline. Same markup as the band docked on
 * an event page, and the island draws the same classes, so the scale lives
 * here rather than in a second template: the newest result is set at marquee
 * size, because it is what a reader with this tab open during a card is
 * waiting for.
 */
.home-live .live {
    margin-top: var(--s-5);
}

.home-live .live__cell {
    padding: var(--s-5) var(--s-6);
}

.home-live .live__tally {
    font-size: var(--t-lg);
}

.home-live .live__line {
    gap: var(--s-2) var(--s-4);
    line-height: 1.05;
}

.home-live .live__name {
    font-size: clamp(1.625rem, 3.6vw, 2.75rem);
    font-weight: 900;
    line-height: 1.02;
}

.home-live .live__name--lost {
    font-weight: 600;
}

.home-live .live__verb {
    font-size: var(--t-sm);
}

.home-live .live__how {
    flex-basis: 100%;
    margin-top: var(--s-2);
    font-size: var(--t-md);
    color: var(--bone);
}

.home-live .live__stamp {
    margin-top: var(--s-3);
}

/* --------------------------------------------------------- the marquee */

/*
 * The event's own title card - the bumper a broadcast runs before the first
 * walkout. Centred, because the event belongs to the whole card rather than to
 * the bout beneath it.
 *
 * The flanking device is the lower third's 18px cut, mirrored: two rules run
 * out to the frame edges and terminate in an angled blade pointing at the
 * title. It is the system's own silhouette used as punctuation, so the header
 * is decorated with the same geometry the name plates are cut from rather than
 * with an ornament borrowed from outside the world.
 */
.marquee {
    text-align: center;
    padding-block: var(--s-5) var(--s-4);
}

/*
 * The title is flanked rather than underlined: a rule runs out to each frame
 * edge and terminates in a solid arrowhead aimed at the event's name. It is
 * the telestrator's own gesture - a line drawn to point at the thing that
 * matters - rather than an ornament borrowed from outside the world.
 */
.marquee__head {
    display: flex;
    align-items: center;
    /* Without its blades (the front page) the title still sits centred. */
    justify-content: center;
    gap: clamp(var(--s-3), 2.5vw, var(--s-6));
}

.marquee__blade {
    position: relative;
    flex: 1 1 40px;
    min-width: 22px;
    height: 12px;
}

.marquee__blade::before {
    content: '';
    position: absolute;
    inset: 5px 0;
    background: var(--rule-bright);
}

.marquee__blade::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 13px;
    background: var(--bone-3);
}

.marquee__blade--l::after {
    right: 0;
    clip-path: polygon(0 0, 100% 50%, 0 100%);
}

.marquee__blade--r::after {
    left: 0;
    clip-path: polygon(100% 0, 0 50%, 100% 100%);
}

.marquee__name {
    font-stretch: 78%;
    font-weight: 900;
    text-transform: uppercase;
    font-size: clamp(1.5rem, 3.4vw, 2.75rem);
    line-height: 1.02;
    letter-spacing: 0.004em;
    text-wrap: balance;
}

.marquee__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: var(--s-2) var(--s-4);
    margin-top: var(--s-3);
    font-family: var(--mono);
    font-size: var(--t-xs);
    letter-spacing: 0.01em;
    color: var(--bone-2);
}

.marquee__meta .sep {
    color: var(--rule-bright);
}

/* The dateline: which card this is, set as broadcast furniture. */
.dateline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2) var(--s-4);
    padding: var(--s-3) var(--s-4);
    border: 1px solid var(--rule);
    background: var(--field-2);
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--bone-3);
}

.dateline b {
    color: var(--bone);
    font-stretch: 82%;
    letter-spacing: 0.08em;
}

.dateline .sep {
    color: var(--rule-bright);
}

/* ------------------------------------------------------------- the odds */

/*
 * The betting line, given its own place in the verdict area because it is the
 * first thing anyone checks a finished fight against.
 *
 * There is no odds data yet - no table, not even an empty one - so every slot
 * here renders keyed out. It is laid in now so the shape is settled and so the
 * absence is visible rather than silent.
 */
.odds {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 1px;
    background: var(--rule);
    border: 1px solid var(--rule);
}

.odds__cell {
    margin: 0;
    flex: 1 1 132px;
    background: var(--field-2);
    padding: var(--s-3) var(--s-4);
}

.odds__cell dt {
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--bone-3);
    margin-bottom: 5px;
}

.odds__cell dd {
    margin: 0;
    font-family: var(--mono);
    font-size: var(--t-md);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
}

/* --------------------------------------------------------- data slate */

/* The corpus stating its own size. A slate, not a row of hero numbers: the
   figures sit in a ruled readout beside the prose that explains them. */
.slate {
    border: 1px solid var(--rule);
    background: var(--field-2);
}

.slate__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-4);
    padding: var(--s-3) var(--s-4);
    border-bottom: 1px solid var(--rule);
}

.slate__row:last-child {
    border-bottom: 0;
}

.slate__row dt {
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--bone-3);
}

.slate__row dd {
    margin: 0;
    font-family: var(--mono);
    font-size: var(--t-lg);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.04em;
}

.duo {
    display: grid;
    gap: var(--s-6);
    align-items: start;
}

/* Blockified min-width for the same reason .dist__meter carries one: a grid
   item's automatic minimum is min-content, and the bout-method sheet's 660px
   scroll floor otherwise becomes the whole column's width on a phone -
   pushing every meter's figure off the right edge of the screen instead of
   letting the sheet scroll inside its own container. */
.duo > div {
    min-width: 0;
}

@media (min-width: 880px) {
    .duo {
        grid-template-columns: 1.15fr 0.85fr;
        gap: var(--s-8);
    }
}

/* --------------------------------------------------------- comparison */

/* Two-sided totals, growing from a centre rule. */
.vs-row {
    display: grid;
    grid-template-columns: 1fr minmax(96px, auto) 1fr;
    align-items: center;
    gap: var(--s-3);
    padding: 9px 0;
    border-bottom: 1px solid var(--rule);
}

.vs-row:last-child {
    border-bottom: 0;
}

.vs-row__label {
    text-align: center;
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--bone-3);
}

.vs-row__side {
    display: flex;
    align-items: center;
    gap: var(--s-3);
}

.vs-row__side--a {
    flex-direction: row-reverse;
}

.vs-row__fig {
    font-family: var(--mono);
    font-size: var(--t-md);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    flex: none;
    min-width: 4.5ch;
}

.vs-row__side--a .vs-row__fig {
    text-align: right;
}

.vs-row__meter {
    flex: 1 1 auto;
    height: 10px;
    background: var(--field-3);
    display: flex;
    min-width: 0;
}

.vs-row__side--a .vs-row__meter {
    justify-content: flex-end;
}

.vs-row__fill {
    height: 100%;
}

.vs-row__fill--a {
    background: var(--side-a);
}

.vs-row__fill--b {
    background: var(--side-b);
}

.vs-row__side--a .vs-row__fig[data-lead] {
    color: var(--side-a);
}

.vs-row__side--b .vs-row__fig[data-lead] {
    color: var(--side-b);
}

/*
 * Narrow enough and the two meters have a couple of dozen pixels between them,
 * which is not a comparison - it is decoration crowding the figures that are
 * doing the actual work. Below this the row is numbers only.
 */
@media (max-width: 560px) {
    .vs-row {
        grid-template-columns: 1fr minmax(80px, auto) 1fr;
    }

    .vs-row__meter {
        display: none;
    }
}

/* Landed over attempted, kept beside the meter so accuracy never has to be
   inferred from the bar alone. */
.vs-row__raw {
    font-family: var(--mono);
    font-size: var(--t-2xs);
    color: var(--bone-3);
    flex: none;
    letter-spacing: -0.02em;
    white-space: nowrap;
}

/* --------------------------------------------------- one-sided meters */

/* A single fighter's distribution - where the strikes went, and from where.
   One row per bucket, so the shape of a career is readable as a block. */
.dist {
    display: grid;
    gap: 1px;
}

.dist__row {
    display: grid;
    grid-template-columns: minmax(84px, 128px) 1fr auto;
    align-items: center;
    gap: var(--s-3);
    padding: 9px 0;
    border-bottom: 1px solid var(--rule);
}

.dist__row:last-child {
    border-bottom: 0;
}

.dist__label {
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--bone-2);
}

/* Both of these are spans, so they need blockifying explicitly - left inline,
   the track still paints its line box and looks correct while the fill inside
   it silently collapses to nothing. */
.dist__meter {
    display: flex;
    height: 12px;
    background: var(--field-3);
    min-width: 0;
}

.dist__fill {
    display: block;
    height: 100%;
    background: var(--bone);
}

.dist__fig {
    font-family: var(--mono);
    font-size: var(--t-sm);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    color: var(--bone-2);
    white-space: nowrap;
}

.dist__fig b {
    color: var(--bone);
    font-weight: 500;
}

/* ------------------------------------------------------- round scrubber */

/* Broadcast graphics change with the round; so does this. The tabs are the
   rounds, and the meters under them redraw for whichever is selected. */
.scrub {
    display: flex;
    flex-wrap: wrap;
    gap: 1px;
    background: var(--rule);
    border: 1px solid var(--rule);
    margin-bottom: var(--s-5);
}

.scrub__tab {
    flex: 1 1 auto;
    min-width: 62px;
    padding: 10px var(--s-3);
    background: var(--field-2);
    border: 0;
    color: var(--bone-3);
    font-family: var(--mono);
    font-size: var(--t-xs);
    font-weight: 500;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background 140ms ease, color 140ms ease;
}

.scrub__tab:hover {
    background: var(--field-3);
    color: var(--bone);
}

.scrub__tab[aria-selected='true'] {
    background: var(--bone);
    color: #0a0e12;
    font-weight: 700;
}

.scrub__tab[data-mark='true'] {
    box-shadow: inset 0 -3px 0 var(--tele);
}

/* ------------------------------------------------------- the nameplate */

/* The full-width lower third a broadcast throws up on a walkout. */
.nameplate {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--s-3) var(--s-5);
}

.nameplate h1 {
    font-size: var(--t-mega);
}

.nameplate__alias {
    color: var(--tele);
    font-stretch: 82%;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: var(--t-xl);
    padding-bottom: 8px;
}

.nameplate__rec {
    font-family: var(--mono);
    font-size: var(--t-xl);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.05em;
    padding-bottom: 4px;
    margin-left: auto;
}

.nameplate__rec small {
    display: block;
    font-family: var(--sans);
    font-size: var(--t-2xs);
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--bone-3);
    text-align: right;
    margin-top: 4px;
}

/* ------------------------------------------------------------- rundown */

/* An event's card, in card order, the way a rundown sheet lists it. */
.rundown {
    border: 1px solid var(--rule);
    border-bottom: 0;
}

/* The block a bout sat in: main card, prelim, early prelim.
 *
 * The first thing this site has been able to say about where a broadcast
 * split its card, and it is an observation rather than an inference - which
 * is why it is a heading over a group and not a badge on a row. Main event
 * and co-main stay in `.bout__slot`, because those are still derived from
 * sitting first and second.
 *
 * It reads as a strap across the sheet rather than a card header: a rule
 * above, the label sitting on the raised plate, no radius and no fill beyond
 * the tonal step. Most of the archive has no segment at all, so this renders
 * on a minority of events and must not restyle the ones without it. */
.rundown__segment {
    font-family: var(--sans);
    font-size: var(--t-2xs);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    line-height: 1.2;
    color: var(--bone-2);
    background: var(--field-3);
    padding: var(--s-3) var(--s-4);
    margin: 0;
    border-bottom: 1px solid var(--rule);
}

/* The top of the sheet already has the rundown's own border. A segment strap
   opening the list would double it. */
.rundown__segment:first-child {
    border-top: 0;
}

.bout {
    display: grid;
    grid-template-columns: 108px 1fr auto;
    align-items: center;
    /* Row gap is tight because the only second row a wide sheet ever grows is
       the provenance stamp, which belongs to the line above it. */
    gap: var(--s-1) var(--s-4);
    padding: var(--s-3) var(--s-4);
    border-bottom: 1px solid var(--rule);
    background: var(--field-2);
    transition: background 140ms ease;
}

.bout:hover,
.bout:focus-visible {
    background: var(--field-3);
}

/*
 * The row whose result was read most recently.
 *
 * A step up the tonal ladder, which is how this system marks anything: the
 * sheet sits on field-2 and answers a pointer with field-3, so a marked row
 * sits on field-3 and answers with field-4, and every row still moves exactly
 * one step. Deliberately NOT telestrator yellow - yellow is the analyst's
 * marker, it already circles the round a bout was finished in, and a second
 * meaning for it would leave neither legible.
 *
 * **The tone is not what says which row this is, and it cannot be.** A marked
 * row at rest and any row under the pointer are both field-3, because the
 * ladder has four rungs and this is the second of them. The row says it in
 * words instead - `Newest result, ...` on the stamp - which is what the No
 * Claim Rule asks for anyway: anything a reader is meant to conclude is
 * written. The step is what makes the words findable, not a claim of its own.
 *
 * The row is marked where it already sits. Card order never changes, because a
 * sheet that reorders itself under somebody scrolling it is unreadable at
 * exactly the moment they are watching the card.
 */
.bout--newest {
    background: var(--field-3);
}

.bout--newest:hover,
.bout--newest:focus-visible {
    background: var(--field-4);
}

.bout__slot {
    font-family: var(--mono);
    font-size: var(--t-xs);
    color: var(--bone-3);
    letter-spacing: -0.02em;
    line-height: 1.35;
}

/* Main event and co-main are positional facts, so they are named rather than
   numbered. Everything below them is just its place in the running order. */
.bout__slot b {
    display: block;
    font-family: var(--sans);
    font-size: var(--t-2xs);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--bone);
}

.bout__slot b.is-comain {
    color: var(--bone-2);
}

.bout__names {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px var(--s-3);
    min-width: 0;
}

.bout__name {
    font-stretch: 78%;
    font-weight: 700;
    text-transform: uppercase;
    font-size: var(--t-md);
    letter-spacing: 0.005em;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
}

/* No colour block here any more. The rundown says "def." - the result is in
   the word, and a side hue beside it would only restate it. The side colours
   belong to the bout page, where two lanes of a graphic have to be told
   apart. */

.bout__name--won {
    color: var(--bone);
}

.bout__name--lost {
    color: var(--bone-3);
    font-weight: 500;
}

/* "def." between two names. Mono, because it sits in a column of results and
   the eye should find it at the same x on every row. */
.bout__verb {
    font-family: var(--mono);
    font-size: var(--t-2xs);
    letter-spacing: -0.02em;
    color: var(--bone-3);
}

.bout__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2) var(--s-3);
    width: 100%;
    margin-top: 5px;
    font-size: var(--t-2xs);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-weight: 700;
    color: var(--bone-3);
}

/* Where a provisional result came from, and when it was read.
 *
 * Mono, because it carries a clock reading and clock readings get compared
 * down a column; sentence case, because it is a sentence and the rest of this
 * strip is not. It is the whole of how "this is ahead of the record" is said -
 * there is no hue for it, and there is not going to be one. */
.bout__stamp {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--s-2);
    grid-column: 2 / -1;
    font-family: var(--mono);
    font-size: var(--t-2xs);
    font-weight: 500;
    letter-spacing: -0.02em;
    color: var(--bone-3);
}

/* **Two facts, so two items: that this row is the newest arrival, and where its
   value came from.** They were one string reading "Newest result, as Wikipedia
   had it at 10:10 UTC", which is two claims and one dangling preposition for a
   translator to reorder. The mark takes one step up the tonal ladder so the
   pair reads as a pair; there is no colour in this, and there is not going to
   be one.

   Flex rather than a comma and a space, because `../lib/rundown.js` draws this
   same stamp on every poll and whitespace between two elements is one thing a
   template and a DOM builder cannot agree on by accident.

   **Label type, not the mono it inherits from `.bout__stamp`.** "Newest result"
   is prose - there is no figure in it and nothing to compare down a column -
   and the Comparable Column Rule has one other clause: mono is never used to
   make something look technical. The band prints the same words 24px above as
   `.live__label`, in Archivo, so inheriting mono here put one label in two
   faces on one screen. What is mono in this strip is the clock reading beside
   it, which is exactly what the rule is for. */
.bout__mark {
    font-family: var(--sans);
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--bone-2);
}

/* An undecided bout's method, where the names carry no "def." to hang it on. */
.bout__how {
    color: var(--bone-2);
}

.bout__end {
    text-align: right;
    font-family: var(--mono);
    font-size: var(--t-xs);
    color: var(--bone-2);
    letter-spacing: -0.02em;
    line-height: 1.4;
    white-space: nowrap;
}

.bout__end b {
    display: block;
    color: var(--bone);
    font-weight: 700;
}

/* "Booked" and "Cancelled" are states of a bout rather than results of one, so
   they sit back a tone from the methods in the same column. */
.bout__end .bout__state {
    color: var(--bone-3);
}

@media (max-width: 720px) {
    .bout {
        grid-template-columns: 84px 1fr;
        row-gap: var(--s-2);
    }

    .bout__end {
        grid-column: 2;
        text-align: left;
    }
}

/* ---------------------------------------------------------- the live band */

/*
 * A card arriving, docked beneath the marquee.
 *
 * The same ruled strip the odds cells use, one tonal step forward onto
 * field-3, because it is the part of the page that is changing. No new colour
 * and no new component: everything here is a label, a mono figure and a 1px
 * rule, which is the whole vocabulary the rest of the sheet is built from.
 *
 * It says what has been recorded and when it was read, and nothing else. There
 * is no clock and no bout-in-progress marker anywhere in it, because no source
 * this site reads publishes either.
 */
.live {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 1px;
    margin-top: var(--s-5);
    background: var(--rule);
    border: 1px solid var(--rule);
}

.live__cell {
    display: flex;
    flex: 3 1 260px;
    flex-direction: column;
    justify-content: center;
    min-width: 0;
    background: var(--field-3);
    padding: var(--s-3) var(--s-4);
}

/* Narrower, so the count and the result do not read as two equal halves: the
   result is the thing somebody came back to the tab for. Both wrap to full
   width on a phone, which is where this gets read during a card. */
.live__cell--tally {
    flex: 1 1 168px;
}

.live__label {
    margin: 0 0 6px;
    font-family: var(--sans);
    font-size: var(--t-2xs);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--bone-3);
}

/* The figures. Mono with tabular numerals, because each of these is a value a
   reader compares against the one above it. The names inside `.live__line` are
   not, and are set back to Title type below. */
.live__tally,
.live__stamp {
    margin: 0;
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
}

.live__tally {
    font-size: var(--t-md);
    font-weight: 500;
    line-height: 1.3;
    color: var(--bone);
}

/* The rundown's own grammar, so the band and the sheet beneath it read as one
   sentence: the winner leads in bone, "def." sits between, the loser follows
   back a tone. No side colour, here or there - the result is in the word. */
.live__line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px var(--s-3);
    margin: 0;
    line-height: 1.4;
}

/* **Title type, not mono**, exactly as `.bout__name` two inches below on the
   same page. A fighter's name is not a value anybody compares down a column,
   and mono is never used to make something look technical. What is mono here
   is the verb between the names and the method beside them, which is where
   `.bout__verb` already puts it. */
.live__name {
    font-family: var(--sans);
    font-stretch: 78%;
    font-size: var(--t-md);
    font-weight: 700;
    letter-spacing: 0.005em;
    text-transform: uppercase;
    color: var(--bone);
}

/* Both only on a bout somebody won. A `vs` line sets neither name back, because
   neither of them lost - which is why the winner's tone is stated here rather
   than left to `.live__name`, exactly as `.bout__name--won` states it in the
   rundown. A modifier the markup emits and no rule matches is a rule somebody
   deleted or forgot to write, and reading the CSS cannot tell you which. */
.live__name--won {
    color: var(--bone);
}

.live__name--lost {
    font-weight: 500;
    color: var(--bone-3);
}

.live__verb,
.live__how {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-size: var(--t-xs);
    font-weight: 500;
    letter-spacing: -0.02em;
    text-transform: uppercase;
    color: var(--bone-3);
}

.live__stamp {
    margin-top: 6px;
    font-size: var(--t-2xs);
    font-weight: 500;
    color: var(--bone-3);
}

/* **The results in a group are one list, and the credit above them is part of
   the heading.** One read carrying several results is the first poll of a card
   the poller started late on. Interleaved at 6px throughout it read as six
   equal rows, so the lines take a step of their own; the group's credit sits
   tight under the label instead, which is the only placement where a line
   dating three results plainly belongs to the first of them as much as to the
   last. A group of one is untouched: its credit follows its line at 6px, as it
   always has. */
.live__stamp + .live__line,
.live__line + .live__line {
    margin-top: var(--s-3);
}

.live__stamp--group {
    margin-top: 0;
}

/* Where the card as a whole stands, in prose: Archivo, because there is no
   figure in it and the Comparable Column Rule has no third case. It reads as
   the closing sentence of the cell whether it follows a result or stands alone
   in an empty one, so it takes the same step off the lines that the lines take
   off each other. */
.live__note {
    max-width: 62ch;
    margin: 0;
    font-size: var(--t-sm);
    line-height: 1.55;
    color: var(--bone-2);
}

.live__line + .live__note,
.live__stamp + .live__note {
    margin-top: var(--s-3);
}

/* ---------------------------------------------------- a result arriving */

/*
 * The angled lower-third wipe, and the only motion in the system that nobody
 * asked for.
 *
 * The motion note above says there is no entrance motion, and this does not
 * reopen it. That rule is about furniture announcing itself on page load, which
 * held the reader's own content at opacity 0 for up to a second and bought a
 * flourish nobody asked to see twice. This is the opposite case and the only
 * one on the site: something the reader is waiting for has changed while they
 * were looking somewhere else on the page, and nothing they did caused it. A
 * change with no gesture behind it has to be findable, and it plays at most
 * once per result on one card a week.
 *
 * The shape is the one the world already has. `.third` is a plate cut at 18px
 * on its trailing edge, the silhouette a caption generator has put a fighter's
 * name in since the broadcast truck existed; the wipe is that cut travelling
 * across the row, so the leading edge of the reveal is the same angle. No new
 * colour, no new component, and nothing moves out of place: the row is where it
 * was before and after, which is what stops a sheet rearranging itself under
 * somebody scrolling it.
 *
 * **The end state clips nothing.** The top-right vertex sits at 100% + 18px, off
 * the element, so the whole box is inside the polygon and `both` can hold it
 * there without shaving the corner off the row for good.
 */
@keyframes wipe-in {
    from {
        clip-path: polygon(0 0, var(--cut) 0, 0 100%, 0 100%);
    }

    to {
        clip-path: polygon(0 0, calc(100% + var(--cut)) 0, 100% 100%, 0 100%);
    }
}

.is-arriving {
    animation: wipe-in 420ms cubic-bezier(0.16, 0.84, 0.24, 1) both;
}

/* Stated here as well as in the global block above, because that one shortens
   an animation to 1ms and this one has no business running at all. */
@media (prefers-reduced-motion: reduce) {
    .is-arriving {
        animation: none;
    }
}

/* ------------------------------------------------------- career ladder */

/*
 * A fighter's whole career as one strip: every bout a block, wins solid,
 * losses hollow, the block's height the significant strikes they landed.
 * Reading left to right is reading the career.
 */
.ladder {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 240px;
    padding-top: var(--s-4);
    border-bottom: 1px solid var(--rule-bright);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
    scrollbar-color: var(--rule-bright) transparent;
}

.ladder__bout {
    flex: 1 1 0;
    min-width: 11px;
    max-width: 38px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    height: 100%;
    position: relative;
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
}

.ladder__stem {
    width: 100%;
    min-height: 4px;
    background: var(--field-4);
    transition: background 140ms ease, box-shadow 140ms ease;
}

/* A win is solid, a loss is hollow. The pair has to survive being 12px wide,
   so the loss carries a bright outline over an empty middle rather than a
   slightly darker fill, which reads as noise at this size. */
.ladder__bout--win .ladder__stem {
    background: var(--bone);
}

.ladder__bout--loss .ladder__stem {
    background: rgba(255, 255, 255, 0.04);
    box-shadow: inset 0 0 0 2px var(--rule-bright);
}

.ladder__bout--draw .ladder__stem {
    background: var(--bone-3);
}

.ladder__bout:hover .ladder__stem,
.ladder__bout:focus-visible .ladder__stem {
    background: var(--tele);
}

.ladder__foot {
    height: 4px;
    width: 100%;
    margin-top: 2px;
    background: var(--rule);
}

.ladder__bout--ko .ladder__foot {
    background: var(--ko);
}

.ladder__bout--sub .ladder__foot {
    background: var(--sub);
}

.ladder__bout--dec .ladder__foot {
    background: var(--rule-bright);
}

.ladder-key {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-5);
    padding-top: var(--s-3);
}

.ladder-key span {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: var(--t-2xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-weight: 700;
    color: var(--bone-3);
}

.ladder-key i {
    width: 14px;
    height: 4px;
    flex: none;
}

/* ------------------------------------------------------------- spec row */

/* The tale of the tape, set as a spec block rather than a card grid. */
.spec {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(128px, 1fr));
    border-top: 1px solid var(--rule);
    border-left: 1px solid var(--rule);
}

.spec > div {
    border-right: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
    padding: var(--s-3) var(--s-4);
    background: var(--field-2);
}

.spec dt {
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--bone-3);
    margin-bottom: 5px;
}

.spec dd {
    margin: 0;
    font-family: var(--mono);
    font-size: var(--t-md);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
}

/* The metric value beside an imperial one. Inline by default so it can sit in
   a sentence; stacked inside a spec cell, where it is a second line. */
.alt {
    display: inline-block;
    margin-left: 8px;
    font-family: var(--sans);
    font-size: var(--t-2xs);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--bone-3);
}

.spec dd .alt {
    display: block;
    margin-left: 0;
    margin-top: 4px;
}

/* --------------------------------------------------------- fighter style */

/* The style itself is stated plainly, like any other fact in the tale of the
   tape. The qualifier is a caveat in running prose underneath it, not a
   badge -- it earns no fill, no border and no colour of its own, because a
   derived label is a hedge, not a decoration. */
.fighter-styles {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
}

.fighter-style {
    font-family: var(--sans);
    font-size: var(--t-md);
}

.fighter-style__qualifier {
    display: block;
    margin-top: 2px;
    font-style: italic;
    font-size: var(--t-xs);
    font-weight: 400;
    color: var(--bone-3);
}

/* ------------------------------------------------ style-conditioned record */

/*
 * A fighter's record split by the kind of opponent they met. Root categories
 * carry the bar; the leaves beneath them carry only figures, because five
 * bars where three will do is noise.
 */
.sched {
    display: grid;
    gap: 1px;
}

.sched__row {
    display: grid;
    grid-template-columns: minmax(140px, 1fr) 44px 84px minmax(90px, 240px);
    align-items: center;
    gap: var(--s-3);
    padding: 9px 0;
    border-bottom: 1px solid var(--rule);
}

.sched__row:last-child {
    border-bottom: 0;
}

.sched__row--group .sched__label {
    color: var(--bone);
    letter-spacing: 0.14em;
}

/* The indent is the only thing saying "this sits under the row above", so it
   has to survive a narrow column; padding rather than a margin on the label
   keeps it out of the grid's width calculation. */
.sched__row--leaf .sched__label {
    padding-left: var(--s-4);
    color: var(--bone-3);
    letter-spacing: 0.1em;
}

.sched__label {
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    color: var(--bone-2);
}

.sched__fig,
.sched__rec {
    font-family: var(--mono);
    font-size: var(--t-sm);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    color: var(--bone-2);
    text-align: right;
    white-space: nowrap;
}

.sched__row--group .sched__rec {
    color: var(--bone);
}

/* Blockified for the same reason .dist__meter is: left inline, the segments
   inside collapse while the track still paints its line box. */
.sched__meter {
    display: flex;
    height: 12px;
    min-width: 0;
}

.sched__seg {
    display: block;
    height: 100%;
}

/* The Career Strip's vocabulary, reused rather than reinvented: a win is
   solid, a loss is hollow with a bright outline, a draw is the mid grey. It
   cannot be red/green or red/blue - both corners' colours are spoken for. */
.sched__seg--win {
    background: var(--bone);
}

.sched__seg--loss {
    background: rgba(255, 255, 255, 0.04);
    box-shadow: inset 0 0 0 2px var(--rule-bright);
}

.sched__seg--draw {
    background: var(--bone-3);
}

@media (max-width: 640px) {
    /* The record is the fact and the bar is the illustration, so the bar is
       what goes when there is no room for both. */
    .sched__row {
        grid-template-columns: minmax(0, 1fr) 40px 78px;
    }

    .sched__meter {
        display: none;
    }

    /* The caption explains the bar. Once the bar is gone, so is the caption
       that names its denominator. */
    .sched__caption {
        display: none;
    }
}

/* ------------------------------------------------------- matchup strip */

/*
 * Two lines under the verdict, one per corner, saying what each brought in
 * against the other's kind. Sits beside the odds strip because it answers the
 * same question - what was known before the bell.
 */
.matchup {
    border: 1px solid var(--rule);
    background: var(--field-2);
    padding: var(--s-4);
}

.matchup__row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s-2) var(--s-3);
    padding-block: var(--s-2);
}

.matchup__who {
    font-stretch: 78%;
    font-weight: 700;
    text-transform: uppercase;
    font-size: var(--t-md);
    color: var(--bone);
}

.matchup__fact {
    font-size: var(--t-sm);
    color: var(--bone-3);
}

/* The record and the category are the two facts; both are set in the figure
   face so they read as data rather than as prose emphasis. */
.matchup__fact b {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
    font-weight: 500;
    color: var(--bone);
}

.matchup__note {
    margin-top: var(--s-3);
    padding-top: var(--s-3);
    border-top: 1px solid var(--rule);
    font-size: var(--t-2xs);
    color: var(--bone-3);
    max-width: 68ch;
}

/* --------------------------------------------------------------- table */

.sheet {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--t-sm);
}

.sheet th {
    text-align: left;
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--bone-3);
    padding: var(--s-2) var(--s-3);
    border-bottom: 1px solid var(--rule-bright);
    white-space: nowrap;
}

.sheet td {
    padding: var(--s-3);
    border-bottom: 1px solid var(--rule);
    vertical-align: middle;
}

/* A row lights up because it can be clicked, and only then. The highlight was
   on every sheet row, which meant the per-round stat sheet and the scorecards
   offered a click they had no destination for - the same disconnect as a row
   that is clickable only across its first cell, arriving from the other side.
   `data-row-href` is written by the template on the public site and by
   `frontend/src/rows.js` on the admin, so a page whose script did not load
   draws neither the pointer nor the highlight. */
.sheet tbody tr[data-row-href] {
    cursor: pointer;
    transition: background 140ms ease;
}

.sheet tbody tr[data-row-href]:hover,
.sheet tbody tr[data-row-href]:focus-within {
    background: var(--field-2);
}

.sheet .num {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.03em;
    text-align: right;
    white-space: nowrap;
}

.sheet__scroll {
    overflow-x: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--rule-bright) transparent;
}

/* A dense sheet scrolls sideways rather than compressing its columns until a
   date wraps over three lines. */
.sheet__scroll .sheet {
    min-width: 660px;
}

/* -------------------------------------------------------------- blocks */

.band {
    padding-block: var(--s-8);
}

.band--tight {
    padding-block: var(--s-6);
}

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

.head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-3) var(--s-5);
    margin-bottom: var(--s-5);
}

.head h2 {
    font-stretch: 82%;
    font-weight: 800;
    text-transform: uppercase;
    font-size: var(--t-lg);
    letter-spacing: 0.01em;
}

.head p {
    color: var(--bone-3);
    font-size: var(--t-sm);
    max-width: 56ch;
}

/*
 * The line under a page's own heading, doing the same job as the sentence
 * under a band heading above: it explains the thing over it without competing
 * with it. It needs its own class because `.head` is a two-column flex built
 * for a heading with an action beside it, and a page heading has neither.
 *
 * This is where an ordering rule belongs. A heading that reads "Every card,
 * newest first" has spent the largest type on the page describing a sort.
 */
.subhead {
    color: var(--bone-3);
    font-size: var(--t-sm);
    max-width: 56ch;
    margin-top: var(--s-3);
}

.link-on {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: var(--t-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--bone-2);
    border-bottom: 2px solid var(--rule-bright);
    padding-bottom: 3px;
    transition: color 140ms ease, border-color 140ms ease;
}

.link-on:hover {
    color: var(--tele);
    border-bottom-color: var(--tele);
}

.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
    padding-top: var(--s-5);
    margin-top: var(--s-5);
    border-top: 1px solid var(--rule);
}

/* --------------------------------------------------------------- foot */

.foot {
    border-top: 1px solid var(--rule);
    padding-block: var(--s-6) var(--s-8);
    color: var(--bone-3);
    font-size: var(--t-sm);
}

.foot a {
    color: var(--bone-2);
    border-bottom: 1px solid var(--rule-bright);
}

.foot a:hover {
    color: var(--tele);
    border-bottom-color: var(--tele);
}

/* --------------------------------------------------------------- video */

/* A 16:9 box with the player inside it, so the page does not reflow when the
   iframe loads. Square corners and a 1px rule, like every other plate. */
.video {
    position: relative;
    aspect-ratio: 16 / 9;
    width: 100%;
    background: var(--field-2);
    border: 1px solid var(--rule);
}

.video iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}

/* A round number that is also the way into the footage. It stays the label it
   was - same size, same weight - and picks up the analyst's marker on hover,
   because reaching for a round is exactly what a telestrator is for. */
.rnd__watch {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px solid var(--rule-bright);
    transition: color 140ms ease, border-color 140ms ease;
}

.rnd__watch:hover,
.rnd__watch:focus-visible {
    color: var(--tele);
    border-bottom-color: var(--tele);
}

/* ------------------------------------------------------------- footnote */

/*
 * A caveat about the page rather than a label on it. Sentence case and small,
 * because tracked capitals at this length read as a warning banner - and this
 * is a quiet statement of what the data does and does not carry.
 */
.footnote {
    font-size: var(--t-2xs);
    color: var(--bone-3);
    max-width: 68ch;
}

/* ------------------------------------------------------------- portraits */

/*
 * A fighter's face, or the plate that stands in for one. Four in five have no
 * photograph, so the absent state is built to recede rather than announce
 * itself - the SMPTE bars were tried here and drowned the real photographs.
 */
.portrait {
    /* Inline-level so it sits inside the opener corner's normal flow and
       obeys `.opener__corner--b`'s `text-align: right` at desktop widths,
       moving to the same side as that corner's name and record. */
    display: inline-block;
    /* inline-block defaults to baseline alignment, which reserves space
       under the box for text descenders that never come - an ~8px gap
       measured on the bout page below every portrait. Top-aligning removes
       it without giving up the inline-level box the rule above needs. */
    vertical-align: top;
    flex: none;
    overflow: hidden;
    background: var(--field-3);
}

/*
 * `object-fit` is a backstop, not the crop. Cloudinary is asked for a square
 * already cropped around the detected face, so there is nothing left here to
 * cut. It used to be the crop, back when the delivery URL carried a width and
 * no height: a 960x1543 photograph arrived at its own aspect ratio and this
 * line centred it in a square box, which on a standing photograph frames a
 * torso. Do not treat this as the place that decides the framing - it only
 * knows the size of the box.
 */
.portrait img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.portrait--initials {
    /* inline-grid, not grid: stays inline-level like `.portrait` above while
       still centring the initials inside itself. */
    display: inline-grid;
    place-items: center;
}

.portrait--initials span[aria-hidden] {
    font-family: var(--sans);
    font-stretch: 70%;
    font-weight: 800;
    /* 2.25rem (36px) at the 132px profile plate; scales with the box at
       every other size so the roster's 48px cells don't clip. */
    font-size: calc(var(--portrait-size, 132px) * 0.2727);
    letter-spacing: -0.01em;
    color: var(--bone-3);
}

/* --------------------------------------------------------------- credits */

/*
 * Attribution for every photograph on the page. Generated from what was stored
 * beside each file, never typed.
 */
.credits {
    margin-top: var(--s-7);
    padding-top: var(--s-4);
    border-top: 1px solid var(--rule);
}

.credits ul {
    margin-top: var(--s-3);
    display: grid;
    gap: var(--s-2);
    max-width: 68ch;
}

.credits li {
    font-size: var(--t-2xs);
    color: var(--bone-3);
    line-height: 1.6;
}

.credits a {
    border-bottom: 1px solid var(--rule-bright);
}

.credits a:hover {
    color: var(--bone);
    border-bottom-color: var(--bone-3);
}
