/* ============================================================================
   Playgama Ad — page layer.

   The page is built as a clean spreadsheet: a lettered column strip, numbered
   rows, cell borders, a formula bar and sheet tabs for navigation. The subject
   matter is ad operations — requests, match rate, fill rate, eCPM — which is
   work that actually happens in a sheet, so the form carries the content
   rather than decorating it.

   Deliberately NOT Microsoft Excel: no ribbon, no toolbars, and none of
   Excel's green. Borrowing another company's brand colour on a Playgama page
   would read as a Microsoft product. The chrome is neutral grey-on-white and
   the accent is Playgama purple.

   No invented data. Where a figure is not known it stays an empty cell — in a
   spreadsheet that reads as "your number goes here", which is honest and on
   concept.

   The vendored library in css/ reads --font-body and the semantic colour
   tokens; redefining those below re-skins its components (buttons, FAQ
   accordion, inputs, code block) without per-component overrides.
   ========================================================================= */

:root {
    --font-body: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --font-mono: "DM Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Spreadsheet chrome */
    --paper: #ffffff;
    --ink: #1d1c26;
    /* A step lighter, for a line that is already introduced by the heading
       above it. The one block that wears it is filled, so it is measured
       there rather than on paper: 4.55:1 on the lilac, 5.2 on white — the
       lightest this cool grey goes and still clears AA for body copy. */
    /* Half black, thereabouts. The step figures it paints are 18px at regular
       weight, which AA reads as small text, and they stand on whatever fill the
       process block is carrying rather than on paper — so this is measured
       there. The lightest neutral that clears 4.5 on the peach: 4.52 there,
       5.6 on white. */
    /* Grey text is the ink, thinned. Three steps and no others, so a shade is a
   decision rather than a near-miss: on paper they measure 6.63:1, 3.09:1 and
   2.02:1. Seventy-two carries anything meant to be read; forty-eight is for the
   sheet's own furniture, which should recede; thirty-two is the unfilled field. */
    /* The same idea the other way up, for text on an inked block. */
    --paper-48: rgba(255, 255, 255, 0.48);
    --ink-72: rgba(29, 28, 38, 0.72);
    --ink-48: rgba(29, 28, 38, 0.48);
    --ink-32: rgba(29, 28, 38, 0.32);
    --grid: #e4e4e6; /* cell borders */
    --grid-strong: #c6c6ca; /* sheet outline, header separators */
    --head: #f7f7f8; /* column / row header fill */
    --rule-on-ink: rgba(255, 255, 255, 0.24); /* the same rules, on an inked cell */
    --head-on-ink: rgba(255, 255, 255, 0.06);
    --fill-lawn: #d6ffb4; /* the ground the quick-start picture stands on; positioning and apply take it too */
    --fill-blue: #003de3; /* was sampled from a blue WebGL picture that has since been replaced */
    --mount: #e0e0e2; /* the band a film is set into, a shade off the sheet's own rules */
    --fill-peach: #ffe9e3; /* the ground the process picture stands on, so cell and plate meet */
    --fill-lilac: #f3ecff; /* likewise, for the player experience block */

    /* The typewriter field, on five flat colours. A poster palette rather than
       a naturalistic one: the drawing is printed, not photographed. */
    --art-petal: #ffd400;
    --art-coin: #ff3925;
    --art-face: #1ba643;
    --art-face-alt: #ffd400;
    --art-stem: #1ba643;
    --art-leaf: #1ba643;
    --art-ground: #0134c7;
    --art-soil: #ff76b5;
    --art-sky: #0134c7;
    --art-sun: #ffd400;

    /* Tags: the same three hues as the drawing, the fill let down to a tint so
       a row of them reads as labels rather than as three signals, the type the
       same hue taken the other way — darker and at full saturation. Each pair
       lands near 6.5:1, which clears AA for text this size with room to spare.
       Deeper would read as black and lose the hue the tag is named by. */
    --tag-a: #fff8d4;
    --tag-a-ink: #7d6e26;
    --tag-b: #ffe8f2;
    --tag-b-ink: #a82662;
    --tag-c: #e5f0ff;
    --tag-c-ink: #4460b2;

    /* Playgama purple carries selection, links and CTAs */
    --accent: #9747ff;
    --accent-soft: rgba(151, 71, 255, 0.08);
    --accent-text: var(--accent-purple-text); /* AA-safe purple for small copy */

    /* How deep the sticky bar is. Three things read it — the bar's own height,
       where the letter strip sticks under it, and how far an anchor jump has to
       clear — so they cannot drift apart when it changes. */
    --bar: calc(2 * var(--paper-row));

    --gutter: 56px; /* row-number column */
    --row-h: 34px; /* the sheet's base row height */
    --paper-row: 28px; /* the graph paper's row step */
    /* The fine cell is a quarter of the paper cell on both axes — the same
       relation The Verge's lede has to its page ruling. Every inset below is a
       whole number of these, which is what makes the copy start on a line
       instead of somewhere inside a cell. */
    --fine-row: calc(var(--paper-row) / 4);
    /* 25, not paper-col/4: --paper-col carries a percentage, and a percentage in
       padding resolves against the element's own containing block, so inside a
       cell it would measure a quarter of the cell instead of a quarter of the
       column. 25 is that quarter at the page's design width. */
    --fine-col: 25px;

    --text: 14px; /* body copy and button labels; 16 on a wide screen */

    --cell-top: var(--paper-row); /* 4 fine rows; the row number follows it */
    /* One inset for everything the block holds — copy, tables, pictures all
       start and end on the same line. That line is the block's edge plus this,
       so it is not itself a column line: a table inside therefore divides the
       inset width, not the sheet's columns. */
    --cell-x: var(--fine-col);
    --cell-bottom: var(--paper-row); /* 4 fine rows, as the top — the foot floats up from here */

    /* The sheet is a column grid that stretches rather than a fixed page that
       centres: the count steps down with the viewport and the cells take up the
       slack, so nothing is ever cropped at the sides.
         16 columns · content 10 · row strip
         12 columns · content  8 · row strip
          8 columns · content  6
          4 columns · content  4   (phone: the content is the whole grid)
       The strip is not one of the columns — it completes the grid on the left
       and pushes the columns right, which is why it is a track of its own. */
    --cols: 16;
    --content-start: 5; /* grid line: strip + three columns of margin */
    --content-span: 10;
    --side-cols: 3; /* the columns left over on each side of the content */
    --strip: var(--gutter);
    --tracks: var(--strip) repeat(var(--cols), minmax(0, 1fr));
    /* The width of that side margin, for the bars that are not grids. */
    --side: calc(var(--side-cols) * (100% - var(--strip)) / var(--cols));

    /* re-skin the vendored library */
    --bg-primary: var(--paper);
    --bg-surface: var(--paper);
    --bg-elevated: var(--paper);
    --bg-card: var(--paper);
    --text-primary: var(--ink);
    --text-secondary: var(--ink-72);
    --text-tertiary: var(--ink-72);
    --border-default: var(--grid);
    --border-strong: var(--grid-strong);
}

* {
    box-sizing: border-box;
}

/* The clip-path carrier. Nothing to see, but an inline box still sits on a text
   line, which put twenty-one phantom pixels under the last block. */
.shapes {
    display: block;
}

body {
    position: relative; /* the row-number rail is measured against the page */
    margin: 0;
    /* The tab bar sits over the foot of the page, so its height is kept clear;
       the two rows on top of that are the sheet showing under the last block,
       the way a sheet does not stop at its last filled cell. */
    padding-bottom: calc(44px + 2 * var(--paper-row));
    /* The page itself is the sheet: ruled edge to edge, with the sections lying
       on it as merged cells.
       The column tile is the whole grid — all --cols lines inside one tile —
       and it is anchored to an edge rather than offset. A percentage in
       background-position is measured against the area minus the tile, not
       against the area, so an offset tile lands in the wrong place; anchoring
       sidesteps that arithmetic entirely. */
    background-color: var(--paper);
    background-image:
        repeating-linear-gradient(
            to right,
            var(--grid) 0 1px,
            transparent 1px calc(100% / var(--cols))
        ),
        linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
    background-size:
        calc(100% - var(--strip)) var(--paper-row),
        100% var(--paper-row);
    background-position: right top;
    /* The column layer only tiles downwards: anchored to the right edge it
       starts exactly where the strip ends, and repeating it sideways would
       carry stray lines back across the strip. Rows still run the full width,
       so the strip is ruled like everything else. */
    background-repeat: repeat-y, repeat;
    color: var(--ink);
    font-family: var(--font-body);
    font-size: var(--text);
    line-height: 1.5;
    color-scheme: light;
    font-variant-numeric: tabular-nums;
    -webkit-text-size-adjust: 100%;
}

p {
    margin: 0;
}

/* ==========================================================================
   FORMULA BAR — sticky top chrome: workbook name, formula, CTA
   ========================================================================== */

/* The bar is a purple band the width of the content column, with the sheet
   carrying on either side of it: the chrome is a filled range in the top rows,
   not a strip laid over the page. The logo needs no pill of its own here — it
   is already white artwork on the brand colour. */
.fx {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 12px;
    /* Whole ruled rows, not 48: the chrome above the sheet has to be a whole
       number of rows or every block below it starts off the grid. */
    height: var(--bar);
    /* The bar spans the window, but its two pieces belong to the content
       column: the wordmark starts on the line the copy starts on and the button
       ends on the line it ends on. Both are the side margin plus the cell's own
       inset — the margin alone would leave them short of that line. */
    padding-left: calc(var(--strip) + var(--side) + var(--cell-x));
    padding-right: calc(var(--side) + var(--cell-x));
    /* Opaque from the strip rightwards, because the page scrolls under it:
       paper, then the same ruling the body draws — column lines anchored to the
       right so the strip stays clear of them — and the band on top, cut to the
       content column by hard stops at the very lines the copy below starts and
       ends on. The strip itself is left clear so the row numbers keep counting
       past the chrome instead of disappearing behind it. */
    background-image:
        linear-gradient(
            to right,
            transparent calc(var(--strip) + var(--side)),
            var(--accent) calc(var(--strip) + var(--side)) calc(100% - var(--side)),
            transparent calc(100% - var(--side))
        ),
        repeating-linear-gradient(
            to right,
            var(--grid) 0 1px,
            transparent 1px calc(100% / var(--cols))
        ),
        linear-gradient(to bottom, var(--grid) 1px, transparent 1px),
        linear-gradient(to right, transparent var(--strip), var(--paper) var(--strip));
    background-size:
        100% 100%,
        calc(100% - var(--strip)) var(--paper-row),
        calc(100% - var(--strip)) var(--paper-row),
        100% 100%;
    background-position:
        left top,
        right top,
        right calc(-1 * var(--row-phase, 0px)),
        left top;
    background-repeat: no-repeat, repeat-y, repeat-y, no-repeat;
}

.fx-name {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
}

.fx-name img {
    height: 28px;
    width: auto;
    display: block;
}

/* White button on the purple band — the library's own inverse fill. */
.fx .btn-fill {
    background-color: var(--paper);
    color: var(--accent-text);
    margin-left: auto;
}

.fx .btn-fill:hover {
    background-color: rgba(255, 255, 255, 0.88);
}

/* ==========================================================================
   SHEET — the lettered column strip + numbered rows
   ========================================================================== */

/* One strip for the whole workbook. Repeating it above each section turned the
   page into 14 bands of grey chrome.
   It runs on the row's own tracks, so each character sits in a column of its
   own — the header row a sheet always has, spelling the name instead of
   counting off A to P. Outside the content it is not grey but the ruled page
   carried on — painted rather than transparent, because the strip is sticky
   and anything see-through would have the sections scrolling behind it. */
/* The sheet opens on its column headers — the first row of the page, above the
   bar and scrolling away with the rest of it. */
.sheet-cols {
    /* Exactly one ruled row, border included. */
    height: var(--paper-row);
    display: grid;
    grid-template-columns: var(--tracks);
    background-color: var(--paper);
    background-image: repeating-linear-gradient(
        to right,
        var(--grid) 0 1px,
        transparent 1px calc(100% / var(--cols))
    );
    background-size: calc(100% - var(--strip)) 100%;
    background-position: right top;
    background-repeat: no-repeat;
    border-bottom: 1px solid var(--grid-strong);
}

/* The first span is the corner cell over the rail; the letters take the columns
   after it, one each, edge to edge. Styled like the sheet tabs: the grey fill,
   hairline dividers between the cells, and the one heavier line reserved for the
   strip's own edge against the page. */
.sheet-cols span {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 8px;
    background: var(--head);
    border-right: 1px solid var(--grid);
    font-size: 12px;
    font-weight: 400;
    color: var(--ink-48);
    text-align: center;
}

/* One ruled row of air between the chrome and the first block, so the sheet
   opens on an empty row the way a workbook does. */
section.row:first-of-type {
    margin-top: var(--paper-row);
}

section.row {
    scroll-margin-top: var(--bar); /* the bar is the only thing that sticks */
    /* Two ruled rows of paper between merged cells, so the sheet shows through
       instead of the sections meeting on a shared border. */
    margin-bottom: calc(2 * var(--paper-row));
}

.row {
    display: grid;
    grid-template-columns: var(--tracks);
}

/* The cursor's trail across the paper. Negative z-index puts the layer above
   the sheet's ruling but under everything in the flow, so a lit cell shows on
   the paper around the blocks and never over one. Filled by main.js. */
.trail {
    position: absolute;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
}

.trail i {
    position: absolute;
    display: block;
    /* Up quickly, then a long way down: the sheet should look touched rather
       than lit. Removed by the script when this finishes. */
    animation: cell-glow 700ms ease-out forwards;
}

/* The one element that is not a lit cell: it carries --strip so the script can
   read a width the browser has already resolved. */
.trail-ruler {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--strip);
    height: 0;
    visibility: hidden;
    animation: none;
}

@keyframes cell-glow {
    0% {
        opacity: 0;
    }
    18% {
        opacity: 0.5;
    }
    100% {
        opacity: 0;
    }
}

/* Row numbers — a real sheet counts every ruled row, not every section, so the
   rail runs the whole page rather than one number per merged cell. It is filled
   by main.js, which is the only way to get one number per 28px of page. */
.rownums {
    position: absolute;
    left: 0;
    top: 0; /* the count starts at the sheet's first row, chrome included */
    /* Pinned to the page's own foot and clipping what hangs past it. The rail is
       filled with a whole number of rows, so its last one runs beyond the
       content — and an absolutely positioned overflow drags the scroll area with
       it, which left the page scrolling most of a row past its end. The row is
       still drawn, it is simply cut where the sheet stops. */
    bottom: 0;
    overflow: hidden;
    z-index: 1;
    display: grid;
    grid-auto-rows: var(--paper-row);
    width: var(--strip);
    background: var(--head);
    /* The sheet's own rule, not the heavier one the cells inside use: this edge
       continues the ruling rather than closing the rail off, and at
       --grid-strong it read as a shadow beside the lighter lines. */
    border-right: 1px solid var(--grid);
}

.rownums span {
    display: flex;
    align-items: center;
    justify-content: center;
    /* The same hairline the sheet tabs put between tabs: faint on the header
       fill on purpose. At --grid-strong every row got its own dark line and the
       rail read as a shadow running down the page. */
    border-bottom: 1px solid var(--grid);
    font-size: 12px;
    color: var(--ink-72);
    font-variant-numeric: tabular-nums;
}

/* The section's merged cell — opaque, so it covers the ruling underneath the
   way a merged cell covers the ones it swallowed. */
.cell {
    /* Placed, not auto-flowed: the row number is a ::before that disappears on
       narrow screens, and auto placement would then slide the cell into the
       margin track. */
    grid-column: var(--content-start) / span var(--content-span);
    min-height: var(--row-h);
    padding: var(--cell-top) var(--cell-x) var(--cell-bottom);
    background: var(--paper);
    /* A merged cell is still bounded by the sheet's own rule, and the block
       covers the ruling underneath it. Only the top and left are drawn here:
       the block's right and bottom edges sit on ruled lines that are painted
       outside it, so outlining all four sides put two 1px lines side by side
       there. An inset shadow rather than a border, so the content box keeps its
       width and everything aligned to the cell's inner edge stays put. */
    box-shadow: inset 1px 1px 0 var(--grid);
    --block-fill: var(--paper);
}

/* --- a filled cell ---------------------------------------------------------
   Two sections are filled in, the way a cell gets a fill in a sheet. Everything
   drawn on them flips: the type, the cell's own rule and the rules of the table
   inside it. The code block is left alone — the library keeps it on its own
   dark surface at every theme so its token colours stay AA, and inverting it
   would break that. */

#sdk .cell,
#compatibility .cell {
    background: var(--ink);
    color: var(--paper);
    box-shadow: inset 1px 1px 0 var(--rule-on-ink);
    --block-fill: var(--ink);
}

/* This block takes its fill from the picture standing on it rather than from
   the ink. Only the ground changes: white type is 7.6:1 here, and the label,
   the rules and the bullets are all white at their own alpha, so they carry
   over to the new colour untouched. */
#compatibility .cell {
    background: var(--fill-blue);
    --block-fill: var(--fill-blue);
}

/* The closing line is a footnote, sized and thinned like the ones in the cases.
   Chosen with the number on the table: 48% white on this blue is 2.78:1, under
   AA — it was raised to 72% once for that reason and deliberately put back. */
#compatibility .txt {
    color: var(--paper-48);
    font-size: 12px;
}

/* The opening reads from the middle rather than off the left edge. Three things
   hold a line left in a stack: the cell's own alignment, the label — which the
   stack pins to the start — and the row of buttons, which is a flex line of its
   own and answers to justify rather than to text-align. */
#hero .cell {
    text-align: center;
    padding-top: calc(2 * var(--paper-row));
}

#hero .sheet-name {
    align-self: center;
}

/* The opening line is set to a narrower measure than the block it sits in —
   centred type reads badly at full width, both edges being ragged. */
#hero .sub {
    max-width: 80%;
    margin-inline: auto;
}

/* Both buttons to the width of the wider one. A grid rather than a flex line:
   1fr columns under max-content sizing all take the widest item, so the pair
   comes out even and the row still hugs them rather than filling the block. */
#hero .actions {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    width: max-content;
    margin-inline: auto;
}

/* The opening picture keeps its own proportions rather than being cut to the
   16:9 every other plate holds. It is not a screenshot of a window like they
   are — it is a photograph, and the 16:9 crop took a tenth of its width off
   the sides, which is where the chain and the last clip lie. */
#hero .pic {
    aspect-ratio: auto;
}

/* Two ruled rows between the buttons and the picture, where the stack's own
   16px sits everywhere else — the picture closes the opening rather than
   continuing it. The stack's gap is still under this, so the margin carries
   only the difference, the way the other steps in .stack are written. */
#hero .actions + .pic {
    margin-top: calc(2 * var(--paper-row) - 16px);
}

/* The certification block is a badge and its caption, not a run of reading —
   it centres on the mark the way a stamp does. The label has to be pulled back
   too: the stack pins every section label to the left edge. */
#mcm .cell {
    text-align: center;
    /* A row more air above the mark than the other blocks take: it opens on a
       badge rather than on a line of type, and the label needs room to read as
       a caption over it rather than as a lid on it. */
    padding-top: calc(2 * var(--paper-row));
}

#mcm .sheet-name {
    align-self: center;
}

/* Everything in the block holds a column narrower than the block itself, so
   the centring reads as centring rather than as full-width copy that happens
   to be ranged middle. The label is shrink-wrapped already and needs no cap. */
#mcm .title,
#mcm .sub,
#mcm .badge {
    max-width: 80%;
    margin-inline: auto;
}

/* Under a case metric the line is the figure's caption, not a statement of its
   own — the same second ink says so, and it sits closer than the 8 a bold name
   takes above the sentence that explains it. */
#case .tcell .txt {
    color: var(--ink-72);
}

#case .tcell .k + p {
    margin-top: 4px;
}

/* Each partner's mark, left of the name. The run ends on a blank cell, which
   has no name to mark and so keeps none. */
#partners .tcell:not(:empty) {
    flex-direction: row;
    align-items: center;
    /* The cell centres its content down the column; turned on its side that
       would centre it across the cell, and the names read from the left. */
    justify-content: flex-start;
}

#partners .mark {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    margin-right: 12px;
    /* Drawn at 80 and shown at 40, so it holds up on a dense screen. */
}

/* A two-part list — a heading with the line that explains it — where a table of
   two columns would otherwise have been. It keeps the rules every other list on
   the sheet has, stacks instead of reading across, and drops the bullet: a
   marker beside a heading this size reads as a slip. */
.ruled.stacked li {
    display: block;
}

.ruled.stacked li::before {
    content: none;
}

/* No rule to open on: each item starts with a heading, which is opening enough,
   and what the list follows — a table, a picture — has closed on a line of its
   own already. The bulleted lists keep theirs; their one-line items have
   nothing else to start them. */
.ruled.stacked li:first-child {
    border-top: 0;
}

/* The heading opens the item rather than a new run inside one, so it drops the
   step a sub-heading carries above it. */
.ruled.stacked .title-sm {
    margin-top: 0;
}

/* Under the heading the line is the explanation rather than a statement of its
   own — the same second ink the other explanatory copy takes. 7.0:1. */
.ruled.stacked .title-sm + .txt {
    margin-top: 8px;
    color: var(--ink-72);
}

/* In the player notes the explanation sits flush under its heading — the
   heading's own line height is all the parting the pair needs. */
#players .ruled .title-sm + .txt {
    margin-top: 0;
}

/* The formats read down the page rather than across a table: each placement is
   one item — name, what it is, the picture, then two ruled rows of air before
   the next. The air sits inside the item, so the rule that closes it lands two
   rows below the picture rather than tight under it. */
/* A row less at the foot than the other blocks take. The last placement already
   closes on a row of its own, so the block was parting twice. Written as the
   authored value rather than the final one: the script adds whatever the
   rounding needs on top of this to land the block on the ruling. */
#formats .cell {
    padding-bottom: 0;
}

#formats .ruled li {
    padding-bottom: var(--paper-row);
    /* No rule under the item: it would fall directly under a picture, which
       closes the placement by itself. The row of air does the parting. */
    border-bottom: 0;
}

/* Tags under a placement's name: what it can serve. A row of pills one ruled
   cell deep, so the line they sit on is the sheet's own. */
.tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}

.tag {
    display: inline-flex;
    align-items: center;
    height: var(--paper-row);
    padding: 0 12px;
    border-radius: 999px; /* a pill: the radius only has to beat half the height */
    font-size: 12px;
}

.tag-a {
    background: var(--tag-a);
    color: var(--tag-a-ink);
}
.tag-b {
    background: var(--tag-b);
    color: var(--tag-b-ink);
}
.tag-c {
    background: var(--tag-c);
    color: var(--tag-c-ink);
}

/* The description keeps the step and the grey it had when it followed the
   heading directly — the tags come between them now. */
.tags + .txt {
    margin-top: 12px;
    color: var(--ink-72);
}

/* Here the description is not a footnote to the heading — it is what the
   placement is, the only sentence a reader gets before the picture. It takes
   the full ink, the same the section's own opening line takes. */
#formats .txt {
    color: var(--ink-72);
}

/* The picture stands off the description the way a sub-heading stands off the
   copy above it — the ones that exist and the ones still waiting alike, and the
   films with them. */
#formats .pic,
#formats .pix-slot,
#formats .window,
#formats .plate {
    margin-top: 16px;
}

/* The plate carries the picture and the player that sits on it. The step above
   belongs to the plate now, not to the picture inside it — otherwise the
   picture would slide down within the plate and take the player with it. */
#formats .plate {
    position: relative;
    overflow: hidden; /* so the player has somewhere to go when it is closed */
}

#formats .plate > .pic {
    margin-top: 0;
}

/* Every measurement inside the player is its Figma number over the frame it was
   drawn in — screen 486x300, insets 30, icons 32, the close button 48 — so the
   whole thing is a percentage of one width and holds its proportions at any
   size. The width itself is 36% of the picture, which is where the placement
   was drawn on the plate.

   A fixed pixel size was the other way, and it does not survive the page: the
   content column is 50% of the window at 1600 and 100% at 599, so one number
   would read as 36% on a desktop and half the picture on a laptop. */
.player {
    position: absolute;
    right: 16px;
    bottom: 16px;
    width: 36%;
    aspect-ratio: 486 / 335;
    color: #fff;
    /* Coming back it overshoots a little, the way something dropped back into
       place does; leaving, it just goes. */
    transition: transform 420ms cubic-bezier(0.34, 1.56, 0.64, 1);
}

.player.is-away {
    transform: translateY(calc(100% + 40px));
    transition: transform 260ms cubic-bezier(0.4, 0, 1, 1);
}

/* The screen is a superellipse, not a rounded rectangle — the corners sweep far
   wider than any border-radius draws, which is the shape's whole character.

   It is clipped to real curves rather than to a polygon: a polygon of this
   shape shows its segments as flat facets along the corner once the player is
   drawn at any size, and how visible they are changes with the width, which is
   what a polygon always does. The path lives in index.html in objectBoundingBox
   units, so it is written once in fractions of the box and stretches with the
   element. Three cubics to a corner, fitted to the mask off the Figma file:
   0.2px from it at the size this renders, and smooth everywhere.

   The polygon stays underneath as the fallback for anything that cannot follow
   the reference. */
.player-screen {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    aspect-ratio: 486 / 300;
    overflow: hidden;
    /* Nothing in here answers to a click — the mute, the label and the question
       mark are a picture of a player, not the controls of one. */
    pointer-events: none;
    /* prettier-ignore */
    clip-path: polygon(
        0% 31.1%, 0.6% 17.8%, 2.5% 11.7%, 5.6% 7.3%, 10.2% 4%, 16.3% 1.8%,
        24.9% 0.4%, 43.6% 0%, 56.4% 0%, 75.1% 0.4%, 83.7% 1.8%, 89.8% 4%,
        94.4% 7.3%, 97.5% 11.7%, 99.4% 17.8%, 100% 31.1%, 100% 68.9%,
        99.4% 82.2%, 97.5% 88.3%, 94.4% 92.7%, 89.8% 96%, 83.7% 98.2%,
        75.1% 99.6%, 56.4% 100%, 43.6% 100%, 24.9% 99.6%, 16.3% 98.2%,
        10.2% 96%, 5.6% 92.7%, 2.5% 88.3%, 0.6% 82.2%, 0% 68.9%
    );
    clip-path: url(#screen-shape);
}

.player-film {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.player-mute {
    position: absolute;
    left: 6.17%; /* 30 of 486 */
    top: 10%; /* 30 of 300 */
    width: 7.41%; /* 36 */
    opacity: 0.8;
}

.player-row {
    position: absolute;
    left: 6.38%; /* 31 of 486 */
    right: 6.38%;
    bottom: 10%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    opacity: 0.7;
}

/* The one thing here that cannot be a percentage — type has no width to be a
   fraction of. The window column is a known share of the viewport in each
   band, so this tracks 29/486ths of the player to within half a pixel from
   the narrowest phone to the widest desktop. */
.player-ad {
    font-size: clamp(10px, 1.5vw, 18px);
    line-height: 1;
}

.player-info {
    width: 7.55%; /* 32 of the row's 424 */
}

.player-mute svg,
.player-info svg,
.player-close svg {
    display: block;
    width: 100%;
    height: auto;
}

/* Sits over the top right corner, half of it above the screen — the one thing
   here that does answer to a click. */
.player-close {
    position: absolute;
    top: 0;
    right: 0;
    display: grid;
    place-items: center;
    width: 9.88%; /* 48 of 486 */
    aspect-ratio: 1;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.3);
    color: #fff;
    cursor: pointer;
}

.player-close svg {
    width: 50%; /* 24 of 48 */
}

/* The player-experience notes sit under their own headings, so they step back
   a shade further than copy that has to carry itself. */
#players .txt {
    color: var(--ink-72);
}

/* The supporting line sits with the description now, above the picture, so
   the only thing that still needs a step is the placement whose line has not
   been written yet. */
#formats .txt + .empty {
    margin-top: 8px;
}

/* The closing block holds only links, so it is inset equally top and bottom
   rather than carrying the floating foot the reading sections need — which is
   why it opts out of the rounding. A pixel less than the row at each end: the
   table inside draws a 1px border top and bottom, and at a flat 28 the block
   would close 2px past a ruled line. */
#contact .cell {
    padding-block: calc(var(--cell-top) - 1px);
}

/* A colour fill, not an ink one: the copy stays black on it. In #positioning
   the table inside is inked instead, so it reads as a filled range sitting on a
   filled cell — its own type and rules invert, exactly as they do in #sdk.
   The closing block takes the same green, and with it the same rule: on colour
   the accent gives way to ink. Black on this green is 14.6:1. */
#positioning .cell,
#apply .cell {
    background: var(--fill-lawn);
    --block-fill: var(--fill-lawn);
}

/* The field keeps its white — the sheet's grey edge would sink into the
   colour, so it takes an inked one. */
/* The company-type segment.

   The library has a .segment, but it is a tab bar: buttons with role="tab", a
   rounded carbon track and a JS-driven sliding indicator. This is a form field
   — one value, submitted, required, keyboard-reachable — and it stands among
   square ink-edged inputs, so it borrows their edge instead: same 56px as
   .input-m, which is two ruled rows, same 1px ink border, same paper.

   Radios do the work. The choice survives without script, arrow keys move
   through it, and the browser's own "please choose one" arrives on submit. */
.seg-group {
    /* A fieldset arrives with a border, padding and min-width of its own. */
    margin: 0;
    padding: 0;
    border: 0;
    min-width: 0;
}

.seg-group > legend {
    /* A legend sits in the border by default and ignores the flex column it is
       nominally in; floating it makes it behave as the first line of the box. */
    float: left;
    width: 100%;
    padding: 0;
    margin-bottom: 8px;
}

.seg {
    display: flex;
    clear: both;
    border: 1px solid var(--ink);
    background-color: var(--paper);
}

.seg-item {
    display: flex;
    flex: 1;
    min-width: 0;
}

.seg-item + .seg-item {
    border-left: 1px solid var(--ink);
}

/* The radio carries the state and the focus; it just has nothing to show. Kept
   in the layout rather than display:none so it stays focusable and reachable by
   arrow key. The span next to it is the whole visible button — an adjacent
   sibling, so the checked and focused looks need no :has(). */
.seg-item input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.seg-item span {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 54px; /* 56 less the two border pixels */
    padding: 0 8px;
    font-size: 16px;
    line-height: 1.2;
    text-align: center;
    cursor: pointer;
    /* Only the colours move, so a press does not shift the text under itself. */
    transition:
        background-color var(--duration-fast) ease,
        color var(--duration-fast) ease;
}

.seg-item:hover span {
    background-color: var(--mount);
}

.seg-item input:checked + span {
    background-color: var(--ink);
    color: var(--paper);
}

/* Keyboard only: the ring belongs to the choice, not to the invisible input. */
.seg-item input:focus-visible + span {
    outline: 2px solid var(--ink);
    outline-offset: -4px;
}

/* Arrow keys move focus and selection together, so focus sits on the chosen
   item almost always — and the chosen item is filled with ink, where an ink
   ring cannot be seen. On that fill the ring turns to paper. */
.seg-item input:checked:focus-visible + span {
    outline-color: var(--paper);
}

/* The sign-off: the mark stands across both columns with two ruled rows of air
   on each side of it, and takes the full width the merged cell gives it — the
   cell drops its own side padding so "two columns wide" means exactly that. */
/* Two of the sheet's own columns, at every width.

   The paper is ruled into --cols columns and a block spans --content-span of
   them, so one column is the block's width over that span — and the block is
   this cell plus the side padding the table sits inside. Written from the same
   two tokens the layout uses, so it stays two columns when the grid drops from
   sixteen to four rather than needing a figure per breakpoint.

   The link is sized rather than the picture, so the target is the mark and not
   the empty half of the row beside it. Block, too: an inline box would add
   three stray pixels underneath and the two rows of air would stop being two
   rows. */
#mark {
    margin-bottom: 0;
}

/* The block is cut to the mark rather than running the width of the column the
   rest of the page keeps: three of the sheet's own columns across, six of its
   ruled rows down. It starts on the same left edge as every other block. The
   snapping script leaves it alone — data-nosnap on the cell — so the six rows
   are the six rows asked for. */
/* Four of the sheet's columns wide, centred on the content column.

   Both edges land on column lines: every breakpoint gives the content an even
   number of columns, so a four-column box centred inside it is offset by a whole
   number of columns either side. The height is left to the snapping script — the
   cell no longer opts out of it — which rounds the block down onto the row grid
   the way it does every other block. So the block sits on the grid in both
   directions and the drawing keeps its own proportions inside it. */
#mark .cell {
    /* No box: the sign-off stands on the ruled paper itself, not in a cell —
       the drawing is transparent and the sheet's lines pass behind it. The
       geometry stays: the width in whole columns, the centring, the snapped
       height, the row of padding. */
    background: transparent;
    box-shadow: none;
    width: calc(100% * 4 / var(--content-span));
    justify-self: center;
    /* Border-box, so this row of air is taken out of the four columns. */
    padding: var(--paper-row);
}

#mark a {
    display: block;
    width: 100%;
    max-width: 320px;
    margin-inline: auto;
}

/* Width from the block, height from the drawing — and then the height is
   rounded down to whole ruled rows by the layout pass in main.js, so the
   drawing's bottom edge always sits on a line of the sheet. The width follows
   the rounded height (never past the block), so it centres itself. */
.fennec {
    display: block;
    width: 100%;
    height: auto;
    margin-inline: auto;
}

/* The honeypots. Off-screen rather than display:none — some bots skip anything
   with no box at all, and the point is that they do fill these in. */
.trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* What came of pressing Submit. Sits in the row rhythm like any paragraph. */
.form-note {
    margin-top: 16px;
}

/* rose-400 is a mid-tone meant for dark surfaces: on this block's lawn green it
   measures 1.80:1, which is an error message no one can read. The library keeps
   --accent-rose-text for exactly this case — 4.89:1 here. */
.form-note[data-state="bad"] {
    color: var(--accent-rose-text);
}

#apply .textarea {
    border-color: var(--ink);
}

#apply .btn-fill {
    background: var(--ink);
}

/* The closing block's outline button, and the one that ends the questions:
   both sit on paper under a heading rather than in a run of copy, and the
   accent outline read as a second call beside the real one. */
#apply .btn-outline,
#faq .btn-outline {
    color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--ink);
}

/* A ruled row between the last question's rule and the button — the stack's own
   16 sits everywhere else, so the margin carries only the difference. */
#faq .faq-list + .actions {
    margin-top: calc(var(--paper-row) - 16px);
}

/* The library fills an outline button with the accent on hover, which would
   put the purple straight back; it fills with the same ink instead. */
#apply .btn-outline:hover,
#faq .btn-outline:hover {
    background: var(--ink);
    color: var(--paper);
    box-shadow: none;
}

/* Inked like #sdk, so the label and the list's rules invert with everything
   else: white on this ink is 16.5:1. */
#compatibility .sheet-name {
    background: var(--head-on-ink);
    color: var(--paper);
}

#sdk .ruled li,
#compatibility .ruled li {
    border-color: var(--rule-on-ink);
}

#sdk .ruled li::before,
#compatibility .ruled li::before {
    background: var(--paper);
}

/* Filled with the ground its picture stands on, so the plate meets the block.
   An opaque colour rather than the translucent tint this block used to carry:
   that one was 8% of the accent laid as a layer, because a see-through block
   would have let the sheet's ruling read straight through it. */
#quickstart .cell {
    background: var(--fill-lawn);
    --block-fill: var(--fill-lawn);
}

/* Label and header band both in ink, as in #positioning: on this fill the
   accent purple reads as a second colour where the block only needs one, and
   white on ink is 16.5:1 rather than the 4.5 it managed on the purple. */
#quickstart .sheet-name,
#quickstart .trow-head {
    background: var(--ink);
    color: var(--paper);
}

/* The table draws its own shape in ink: the sheet's pale grey goes to nothing
   on a filled cell. The list is parted the same way. */
#quickstart .ruled li {
    border-color: var(--ink);
}

/* The call to action is inked with the rest of the block rather than carrying
   the accent, which on this fill would be the only purple in it. */
#quickstart .btn-fill {
    background: var(--ink);
}

/* A ruled row between the list and the button, where the stack's own 16 sits
   everywhere else — the margin carries only the difference. */
#quickstart .ruled + .actions {
    margin-top: calc(var(--paper-row) - 16px);
}

/* Only the header band and the rules are inked; the cells themselves keep the
   fill showing through, so the table reads as ruled on the colour rather than
   as a black panel dropped onto it. */
#positioning .trow-head {
    background: var(--ink);
    color: var(--paper);
}

/* Inside the inked header the divider is the fill: it cuts the band in two
   rather than disappearing into it. */

/* The label is an inked cell, cut out in the fill — the same pair the table's
   header uses, so the block has one relationship between ink and colour. The
   closing block is on the same green and keeps its accent-tinted label only by
   accident, so it takes the pair too. */
#positioning .sheet-name,
#apply .sheet-name {
    background: var(--ink);
    color: var(--fill-lawn);
}

/* And the same ruled row between the list and the buttons under it. */
#sdk .ruled + .actions {
    margin-top: calc(var(--paper-row) - 16px);
}

#sdk .btn-outline {
    color: var(--paper);
    box-shadow: inset 0 0 0 1px var(--rule-on-ink);
}

#sdk .btn-outline:hover {
    background: var(--paper);
    color: var(--ink);
    box-shadow: none;
}

/* --- inner table — lives inside a merged cell, so it has no row gutter and
   does not touch the page-wide row counter --------------------------------- */

/* The table starts on the block's content line like everything else, and since
   that line is a column line its own columns are the page's columns. Square
   corners: a radius would pull the frame off the line. */
.tbl {
    /* One weight and one colour throughout, everywhere on the page: the frame
       encloses the same kind of line it contains, and a table's lines are
       content dividers rather than sheet furniture, so they are ink where the
       ruling behind them is grey. */
    border: 0.5px solid var(--ink);
    overflow: hidden;
}

.trow {
    display: grid;
    grid-template-columns: repeat(10, minmax(0, 1fr));
    border-bottom: 0.5px solid var(--ink);
}

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

.trow-head {
    background: var(--head);
    border-bottom: 0.5px solid var(--ink);
}

/* The rule between header columns is drawn in the colour the block is filled
   with, whatever that is: on an inked band it cuts the band in two rather than
   disappearing into it, and on a pale one it does the same without adding a
   line the table does not need. Every block states its own fill, so this is one
   rule rather than one per section. */
.trow-head .tcell {
    border-right-color: var(--block-fill);
}

/* Cells stay full height so the rules between them reach the row's edge, and
   the content is centred inside instead — a column flex, so a cell holding a
   name above its description still stacks them. */
.tcell {
    grid-column: span 10;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 12px 14px;
    border-right: 0.5px solid var(--ink);
}

.tcell:last-child {
    border-right: 0;
}

/* A cell that carries more than one thing — a bold name above the text that
   explains it. Same 8px step the eyebrow uses above a title. */
/* The caption under a value: a label for the number above it, not a line of
   copy, so it is set smaller than the body. */
.tcell .k + p {
    margin-top: 8px;
    font-size: 12px;
}

/* Column spans in tenths: a table's columns are the page's columns, so a rule
   inside a table falls on a rule of the sheet. Ten does not divide into thirds
   or quarters, which is why the old twelfths became 4+3+3 and 2+3+3+2. */
.c2 {
    grid-column: span 2;
}
.c3 {
    grid-column: span 3;
}
.c4 {
    grid-column: span 4;
}
.c5 {
    grid-column: span 5;
}
.c6 {
    grid-column: span 6;
}
.c10 {
    grid-column: span 10;
}

/* ==========================================================================
   CELL CONTENT

   One size, one colour, two weights. Everything inside a block is 14px black;
   the only typographic variable left is regular vs bold. Sizes and greys are
   set here once, so no rule below restates them.

   Section titles are the single exception — a block still needs a heading.
   ========================================================================== */

.cell {
    font-size: var(--text);
    line-height: 1.5;
    color: var(--ink);
}

/* Library components set their own size and colour; bring them into the same
   system. Buttons keep the library's Medium — the one place a third weight is
   wanted, so the label reads as a control rather than as copy. */
.cell .btn {
    font-size: var(--text);
}

.cell .faq-a {
    font-size: var(--text);
    color: var(--ink-72);
}

/* Links read as body copy — the browser default blue was the only other
   colour left inside a block. */
.cell a.txt {
    color: inherit;
}

/* The footer's links sit underlined in ink, as a sheet writes its references.
   Pointed at, one goes the other way to every other link here: the underline
   comes off and the name takes the bar's purple — 4.5:1 on white, which holds
   at this size. */
#contact a.txt:hover,
#contact a.txt:focus-visible {
    color: var(--accent);
    text-decoration: none;
}

/* A link that carries something else's type — a heading, say. At rest it is
   that thing: the same ink, no rule under it. The accent and a hairline show
   only on hover, so the page reads as ink until it is pointed at. */
.link {
    color: inherit;
    text-decoration: none;
}

.link:hover,
.link:focus-visible {
    /* the bar's own purple, not the darker one kept for small type: on white
       it is 4.5:1, which holds at this size but leaves nothing to spare */
    color: var(--accent);
    text-decoration: underline;
    text-decoration-thickness: 0.5px;
}

/* Small uppercase labels — section eyebrow, table column headers, form label,
   badge. One exception to the 14px body size, applied in one place so the four
   of them cannot drift apart. */
.sheet-name,
.hd {
    font-size: 10px;
}

/* A third level under the section title — smaller and lighter than the h2, but
   still a heading rather than the 11px caption the table columns use. The other
   thing that heads its own run takes the same type: the case metrics. Twenty
   at 1.4 is exactly one ruled row. */
.title-sm,
#case .tcell .k {
    margin: 0;
    font-size: 20px;
    line-height: 1.4;
    font-weight: 500;
    letter-spacing: -0.2px;
}

/* A sub-heading opens a new run inside the block, so it stands off further than
   the stack's 16 — taken as a margin on top of that gap. A figure sitting in a
   cell opens nothing, which is why this is not in the rule above. */
.title-sm {
    margin-top: 16px;
}

/* The section label is a filled cell of its own — the way a sheet marks a
   heading row. The tint carries the accent while keeping the type dark enough
   to stay AA at 11px; solid purple with white on it would be 3.6:1. */
.sheet-name {
    padding: 6px;
    background: var(--accent-soft);
    color: var(--accent-text);
    font-weight: 700;
    letter-spacing: 0.6px;
    text-transform: uppercase;
}

/* On the inked block the label inverts with everything else — the 6% lift the
   sheet uses on ink. */
#sdk .sheet-name {
    background: var(--head-on-ink);
    color: var(--paper);
}

/* The eyebrow belongs to the title under it, so the pair sits tighter than the
   16 the stack puts between separate blocks. A flex gap cannot vary per pair,
   so the 8 is taken back with a negative margin rather than by lowering the
   stack's gap — which would pull every block in every section together. */
.sheet-name + .title,
.sheet-name + .title-hero {
    margin-top: -8px;
}

.title {
    font-size: 34px;
    line-height: 1.15;
    font-weight: 400;
    letter-spacing: -0.6px;
    margin: 0;
}

.title-hero {
    font-size: 52px;
    line-height: 1.08;
    letter-spacing: -1.4px;
    font-weight: 400;
    margin: 0;
}

/* .sub and .txt no longer differ in size or colour — the distinction is
   carried by weight and position alone. */
.sub,
.txt {
    font-weight: 400;
}

.k,
.hd {
    font-weight: 700;
}

.hd {
    letter-spacing: 0.4px;
    text-transform: uppercase;
}

/* The step tables name their rows a size above the copy but at regular weight
   — a label for the line rather than a heading over it. The row is stated so
   the line still measures exactly one. */
/* h3 — a label for the line that follows rather than a heading over a run. The
   step tables' row names and the player notes both read this way. Worn beside
   whatever class places the thing (.k in a table cell, .title-sm in a list):
   this one carries the type only, and is declared after both so it wins the
   size and the weight without needing to be scoped to a section. */
.h3 {
    font-size: 18px;
    line-height: var(--paper-row);
    font-weight: 400;
    letter-spacing: normal;
}

/* The process steps are numbered. A counter rather than digits in the markup,
   so the sequence cannot fall out of step with the rows if one is moved, and
   decimal-leading-zero supplies the nought without anything having to pad it. */
#how .tbl {
    counter-reset: step;
}

#how .trow:not(.trow-head) {
    counter-increment: step;
}

/* The process cell is filled with the ground its picture stands on, so the
   plate meets the block instead of sitting as a panel on it. */
#how .cell {
    background: var(--fill-peach);
    --block-fill: var(--fill-peach);
}

#players .cell {
    background: var(--fill-lilac);
    --block-fill: var(--fill-lilac);
}

/* The notes are parted in ink rather than in the sheet's grey, which went to
   nothing on a filled cell — and the run closes on the last note rather than on
   a line under it, the way a list ends where its last item does. */
#players .ruled li {
    border-color: var(--ink);
}

#players .ruled li:last-child {
    border-bottom: 0;
}

/* Drawn in ink rather than in the sheet's grey: on a filled cell the pale rules
   went to nothing, and the table has to hold its own shape. The same three
   selectors #positioning uses for the same reason. */
/* The head is inked too, and everything on it inverts. No rule under it — the
   ground already parts it from the steps, and a line as well would make two
   partings where the reading needs one. */
#how .trow-head {
    background: var(--ink);
    color: var(--paper);
    border-bottom: 0;
}

#how .tcell .k::before {
    content: counter(step, decimal-leading-zero);
    margin-right: 10px;
    /* The figure counts the step rather than naming it, so it stands two steps
       back from the name it is counting. */
    color: var(--ink-48);
}

/* The last headered table joins the others: the band is inked, its caption is
   paper, and the table is drawn in ink throughout — frame, rows and columns the
   same colour as the band. The header's own column rule stays the block's fill
   (paper here), cutting the band the way it does everywhere. */
#reporting .trow-head {
    background: var(--ink);
    color: var(--paper);
}

/* These are content dividers, not sheet furniture, so they use full ink. The
   list is opted out of per-item row snapping in the markup, and the block
   itself still lands on the sheet grid. */
#reporting .ruled li {
    padding-block: 16px;
    border-color: var(--ink);
}

#reporting .ruled.stacked .title-sm + .txt {
    margin-top: 4px;
}

/* A new case opens two ruled rows below the line that closed the previous one —
   the same air the sheet keeps between blocks, so the seam reads as a seam.
   The margin carries only the difference over the stack's 16. */
#case .source + .title-sm {
    margin-top: calc(2 * var(--paper-row) - 16px);
}

/* A picture that goes where its heading goes. The anchor is a block so the
   stack treats it exactly as it treated the bare picture. */
.pic-link {
    display: block;
}

/* Footnotes: a source attribution under a case, and the closing line under the
   reporting list — the smallest type on the page at the faintest step of the
   scale. A link inside inherits both; like every .link on the page it shows
   itself only on hover. */
.source,
#reporting .ruled + .txt {
    font-size: 12px;
    color: var(--ink-32);
}

/* The funnel stages are read against the line beside them rather than scanned
   as a column of labels, so they carry no more weight than the copy they name. */
#reporting .tcell .k {
    font-weight: 400;
}

/* A row's name is short and the line explaining it is not, so both read from
   the top of the cell instead of floating in the middle of a tall row. */
#how .trow:not(.trow-head) .tcell,
#positioning .trow:not(.trow-head) .tcell,
#quickstart .trow:not(.trow-head) .tcell {
    justify-content: flex-start;
}

/* A cell that has no value yet. Reads as "your number goes here" rather than
   inventing one. */
.empty::before {
    content: "—";
}

/* Supplied artwork, run the width of the block and left to its own
   proportions. It stands off 32 at both ends — a mark someone else's brand
   owns, given room rather than run in with the copy. */
.badge {
    display: block;
    width: 100%;
    height: auto;
    margin-block: 16px;
}

.actions {
    display: flex;
    flex-wrap: wrap;
    /* Tighter than the stack's 16: buttons side by side are one control group,
       not two blocks that happen to follow each other. */
    gap: 12px;
    align-items: center;
}

/* Buttons share the row instead of sitting at their label width, so a pair
   splits it evenly. They stay side by side for as long as both labels fit and
   only then does the row wrap — no forced full-width stacking on narrow
   screens. */
.actions .btn {
    flex: 0 0 auto;
}

/* Where a pair offers one thing to do and one way round it, the first is drawn
   twice the width of the second. Stated as a ratio rather than a number: the
   row is sized to its content, and under that sizing the 2fr/1fr split resolves
   to the smallest pair that fits both labels — the second button keeps exactly
   the width it had, the first gets double it, and no figure here goes stale
   when a label changes.

   #hero is deliberately not in this list: its pair was set to one width on
   purpose, and centred under a centred line. */
#apply .actions {
    display: grid;
    grid-template-columns: 2fr 1fr;
    width: max-content;
    max-width: 100%;
}

/* A merged cell holding several blocks — a sheet does this too. Spacing comes
   from the stack so the pieces don't need margins of their own. */
.stack {
    display: flex;
    flex-direction: column;
    /* stretch, not flex-start: shrink-to-fit made <pre> size to its longest
       line (pushing the page sideways) and collapsed the fluid pictures. */
    align-items: stretch;
    gap: 16px;
}

/* A table or a list stands further off the copy above it than two runs of copy
   stand off each other — and so does a heading that opens a new run after one.
   Stated as a pair rather than as a rule on every heading, so the eyebrow and
   the title it belongs to keep their own tighter step. */
.stack > .tbl:not(:first-child),
.stack > .ruled:not(:first-child),
.stack > .faq-list:not(:first-child),
.stack > .tbl + .title,
.stack > .ruled + .title,
.stack > .faq-list + .title {
    margin-top: 8px;
}

/* the badge keeps its natural width; the button row must not, or the buttons
   inside it have no room to share */
.stack > .sheet-name {
    align-self: flex-start;
}

.stack > .code-block {
    min-width: 0;
    max-width: 100%;
}

/* Copy stretches with everything else; the measure is set by the section's
   own width, not by a cap on the paragraph. */
.stack .sub {
    max-width: none;
}

/* The opening screen is a single merged cell, so it needs more air between its
   parts than a normal stacked cell — and a little padding around the whole
   block, since there are no neighbouring rows to give it any. */

/* Two blocks of prose side by side inside a merged cell. */
.split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px 32px;
    width: 100%;
}

.apply-form {
    width: 100%;
}

/* One field to a line, so the form reads straight down and every field has the
   whole column to hold a long answer in. */
.field-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 16px;
}

/* The buttons stand a little clear of the last field — not the 16 that parts
   one field from the next, so the form ends rather than continues. */
.field-grid + .actions {
    margin-top: 24px;
}

.input-group {
    margin: 0;
}

/* Square, as the sheet is. The library rounds its fields to 16 and fills them
   with an elevated surface; on a filled cell that surface is invisible, so the
   field takes the paper and the ink edge every other box on this page has, and
   the corners come off. Two ruled rows tall, which is what .input-m already
   measures. */
.input,
.textarea {
    border-radius: 0;
    background-color: var(--paper);
    border: 1px solid var(--ink);
}

/* An example of an answer, not the name of the field — every field here carries
   a label of its own, so this can stand well back. */
.input::placeholder,
.textarea::placeholder {
    color: var(--ink-32);
}

/* Four ruled rows. The library gives a textarea a good deal more, which on a
   form of single-line fields read as a wall. */
#apply .textarea {
    min-height: calc(4 * var(--paper-row));
}

.input:focus,
.textarea:focus {
    background-color: var(--paper);
    border-color: var(--ink);
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

/* The form is a plain block, so the step from the field down to the buttons has
   to be stated — the same 16 the stack uses between blocks. It holds when the
   field is dragged taller, since it is measured from the field's bottom edge. */
.textarea + .actions {
    margin-top: 16px;
}

/* The links close the page rather than continue the form, so they sit further
   down than the stack's step. Taken as a margin on top of the 16 the stack
   already puts there, the way the eyebrow pair takes 8 back. */
.apply-form + .tbl {
    margin-top: 40px;
}

/* A wide picture runs the width of the block and takes 16:9, whatever it was
   shot or drawn at — one frame for every photograph and illustration on the
   page, filled from the middle. Stated here rather than left to each file's
   own proportions: the box has to be right before the picture arrives, or the
   row snapping measures a collapsed one and the block lands off the ruling. */
.pic {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    object-position: center;
}

/* Three of the drawings are 2:1 where the rest are 16:9, so they take their own
   proportions rather than being cut to a frame they do not fit — the opening
   picture already does this. Nothing is cropped, and the rule still holds if
   the shape of one of those files changes again. */
#how .pic,
#players .pic,
#quickstart .pic {
    aspect-ratio: auto;
}

/* Typewriter art: one fixed grid of characters, carried in an SVG so it scales
   with the column instead of with the font. The viewBox holds the 16:9 the
   pictures around it keep; the type is the page's mono, inherited into the
   inline SVG the way any other element inherits it. */
.art {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    font-family: var(--font-mono);
    fill: var(--ink);
}

.a-p {
    fill: var(--art-petal);
}
.a-c {
    fill: var(--art-coin);
}
.a-f {
    fill: var(--art-face);
}
.a-e {
    fill: var(--art-face-alt);
}
.a-s {
    fill: var(--art-stem);
}
.a-l {
    fill: var(--art-leaf);
}
.a-g {
    fill: var(--art-ground);
}
.a-d {
    fill: var(--art-soil);
}
.a-k {
    fill: var(--art-sky);
}
.a-u {
    fill: var(--art-sun);
}

/* A browser window with something running in it: the supplied title bar on top
   and the screen under it. Sixteen by nine overall, like every other picture on
   the page, so the bar takes its own 1600:73 and the screen takes the rest —
   which leaves the screen a little wider than the film, so the film is filled
   into it and loses 36px top and bottom of its own 1080. */
.window {
    display: flex;
    flex-direction: column;
    margin: 0;
    aspect-ratio: 16 / 9;
}

.window-bar {
    display: block;
    width: 100%;
    height: auto;
}

/* What is left of the window once the bar has taken its share: the screen
   itself. The control anchors to this rather than to the whole window, so the
   top of it is the top of the picture whatever proportions the bar image has. */
.window-view {
    position: relative;
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

/* A mount around the film, the way a picture sits in a window. Drawn over the
   film rather than as a border on this box: a border would take the 8px out of
   the content and reshape the screen the film is fitted to, and the frame above
   is cut to a 16:9 window. The sound button keeps its own 12px inset, so it
   clears the band by four. */
.window-view::after {
    content: "";
    position: absolute;
    inset: 0;
    /* Four and a half: the hairline takes the outer half pixel and the mount
       keeps its full four inside it. The line is an outline pulled inward by
       its own width rather than a second border, which would have to come off
       the mount. */
    border: 4.5px solid var(--mount);
    outline: 0.5px solid var(--ink);
    outline-offset: -0.5px;
    pointer-events: none;
}

.window-screen {
    display: block;
    width: 100%;
    /* min-height 0: a flex item will not shrink below its content otherwise,
       and a video's content is its own 1080. */
    flex: 1;
    min-height: 0;
    object-fit: cover;
    /* Held to the bottom edge rather than centred: the screen is wider than the
       film, so something has to go, and what sits along the bottom of an ad —
       the banner, the store button — is the part worth keeping. The top of the
       frame is sky. */
    object-position: center bottom;
    background: var(--ink); /* a screen not yet lit, rather than a hole */
}

/* The control sits inside the screen, top right, and only appears once there is
   something playing to unmute. Top rather than bottom because a creative puts
   its own call to action along the bottom edge — the Pre-roll film has a store
   button there, and the sound toggle landed on top of it. */
/* Two classes deep on purpose: the library's .btn-s sets the padding and the
   type, and the page's own .btn sets the padding again further down this file —
   a single class would lose to both. The height stays the library's 32px. */
.btn.window-sound {
    position: absolute;
    right: 12px;
    top: 12px;
    padding: 0 8px;
    font-size: 12px;
    background: var(--paper);
}

/* White type needs something to be white against. The library fills a ghost
   button with 16% white on hover, which over a playing film would put white on
   whatever frame happens to be underneath; it fills with ink instead — the same
   turn the outline buttons take elsewhere on the page. 16.5:1. */
.window-sound:hover {
    background: var(--ink);
    color: var(--paper);
    box-shadow: none;
}

/* A picture that has not been drawn yet. It holds the space at the shape the
   drawing will take — 16:9, like every other picture on the page — ruled like
   an empty one: a 25 by 14 lattice closed on the
   right and bottom the same way, so the block reads at its final size instead
   of collapsing to a caption. */
.pix-slot {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 16 / 9;
    color: var(--ink-48);
    box-shadow: inset -1px -1px 0 var(--grid);
    background-image:
        repeating-linear-gradient(to right, var(--grid) 0 1px, transparent 1px var(--fine-col)),
        repeating-linear-gradient(
            to bottom,
            var(--grid) 0 1px,
            transparent 1px calc(var(--fine-row) * 2)
        );
}

/* ==========================================================================
   LIBRARY COMPONENTS
   ========================================================================== */

/* Every button is its label wide, inset by 20 — the library's sizes set 16 (12
   on the smallest), so the inset is stated once here rather than per size.
   The 4px square corner this page used is dropped: fully rounded is the
   library's own --radius-rounded, so the pill needs no override at all. */
.btn {
    padding-inline: 20px;
    font-family: var(--font-body);
}

/* A plain list ruled like the sheet — for runs of single values that need no
   columns, where a one-column table was only borrowing the table's frame. */
.ruled {
    margin: 0;
    padding: 0;
    list-style: none;
}

.ruled li {
    display: flex;
    /* The bullet marks the line it opens, so it holds to the top of the item
       and is dropped onto the middle of that first line rather than floating
       between two of them when the text wraps. */
    align-items: flex-start;
    padding: 12px 0;
    border-bottom: 0.5px solid var(--grid);
}

/* Drawn, not a list marker: a flex item has no marker box, and this keeps the
   bullet on the text's centre line at any row height. */
.ruled li::before {
    content: "";
    flex: 0 0 auto;
    width: 5px;
    height: 5px;
    /* Half a line, less half the bullet. Written against the type size rather
       than in pixels because the two bulleted lists on the page are set at
       different sizes; it lands within half a pixel of centre on both. */
    margin-top: calc((1.5em - 5px) / 2);
    margin-right: 12px;
    border-radius: 50%;
    background: var(--ink);
}

.ruled li:first-child {
    border-top: 0.5px solid var(--grid);
}

/* The questions sit straight on one another, parted by their own rule. The
   library holds them 12 apart, which on a ruled sheet reads as a break in the
   ruling rather than as a list. */
.faq-list {
    max-width: none;
    margin-inline: 0;
    gap: 0;
}

.faq-item {
    background: transparent;
    border-radius: 0;
    padding: 12px 0;
    border-bottom: 0.5px solid var(--grid);
}

#faq .faq-item {
    border-color: var(--ink);
}

.faq-item:hover {
    background: transparent;
}

/* The run of questions closes on a rule like every other list on the sheet,
   so the section does not end on an open edge. */
.faq-item:last-child {
    border-bottom: 0.5px solid var(--grid);
}

/* A question sits a size above the answer but at regular weight — it opens the
   run rather than heading it. The row is stated so the line still measures
   exactly one. */
.faq-q-text {
    font-size: 18px;
    line-height: var(--paper-row);
    font-weight: 400;
}

/* Clipped, so the height the answer is opened and closed with actually hides
   the copy instead of letting it spill. */
.faq-a {
    padding-right: 0;
    overflow: hidden;
}

/* An item keeps `open` while it closes — there would be nothing left to animate
   otherwise — so the chevron would wait out the whole close before turning
   back. It turns with it instead. */
.faq-item.is-closing .faq-toggle {
    transform: none;
}

.faq-toggle img {
    filter: invert(1);
}

.code-block {
    border-radius: 4px;
    font-size: 13px;
}

.textarea {
    border-radius: 4px;
    font-family: var(--font-body);
    font-size: var(--text);
    /* Eight ruled rows — 224, not the 223 it was dragged to, so the field is a
       whole number of rows like everything else on the sheet. */
    min-height: calc(8 * var(--paper-row));
}

/* ==========================================================================
   SHEET TABS — the workbook bar doubles as the page navigation
   ========================================================================== */

.tabs {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    display: flex;
    align-items: stretch;
    height: 44px;
    /* The bar is the workbook's own edge, not a block on the sheet: it runs the
       full width and the tabs share it out between them. */
    padding-inline: 0;
    background: var(--head);
    border-top: 1px solid var(--grid-strong);
    overflow-x: auto;
    scrollbar-width: thin;
}

.tabs a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Grow from the label's own width, so the row fills the bar without the
       short names being padded out to match "Compatibility". Below the width
       where they all fit, they fall back to their labels and the bar scrolls. */
    flex: 1 1 auto;
    padding: 0 12px;
    white-space: nowrap;
    font-size: 12px;
    color: var(--ink-72);
    text-decoration: none;
    /* Flat and full height, divided by hairlines — the sheet-tab bar of a
       spreadsheet, not raised paper tabs. */
    border-right: 1px solid var(--grid);
    transition:
        background-color 0.15s,
        color 0.15s;
}

.tabs a:last-child {
    border-right: 0;
}

.tabs a:hover {
    background: var(--paper);
    color: var(--ink);
}

/* The active tab follows the section on screen. main.js moves the class as you
   scroll; the one in the markup is the state before the script runs. It is
   marked the way a spreadsheet marks the open sheet: tinted fill and the
   accent colour, no chrome of its own — and no change of weight, which would
   shift the labels beside it every time the active tab moved. */
.tabs a.is-active {
    background: var(--accent-soft);
    color: var(--accent-text);
}

/* ==========================================================================
   RESPONSIVE
   The grid steps down rather than the columns being cropped: 16 → 12 → 8 → 4,
   with the content taking 12 → 8 → 6 → 4 of them. The lettered strip needs all
   twelve of its letters, so it only stands at the widest step; the row-number
   strip goes one step later, at 960, and takes the left track with it.
   Below 760 the tables stack as well.
   ========================================================================== */

/* Past 1600 the content gives up a column at each side — eight of sixteen
   rather than ten. A screen that wide would otherwise be read across a line
   the width of the monitor, and a fixed cap cannot do this job: the columns
   keep widening while a fixed block does not, so its edges would leave the
   ruling they have sat on all the way down the page. Whole columns keep them
   on it. The block is 772 at this width, 922 near 1900, and grows a column's
   worth every 160 after that. */
@media (min-width: 1600px) {
    :root {
        --content-start: 6;
        --content-span: 8;
        --side-cols: 4;
    }
}

/* Past 1400 there is room for the copy to grow; the blocks re-snap to the
   ruling afterwards, so the extra line height costs nothing. */
@media (min-width: 1401px) {
    :root {
        --text: 16px;
    }

    /* The headings grow with the copy under them, or they close in on it. The
       body goes up by a seventh, and twenty taken up by a seventh is twenty
       three. The row has to be stated at that size: the 1.4 ratio that made
       twenty come out at exactly one ruled row would put twenty three at 32,
       and the heading would stop landing on the ruling. */
    .title-sm,
    #case .tcell .k {
        font-size: 23px;
        line-height: var(--paper-row);
    }

    /* The step tables' row names keep their step below the headings. */
    .h3 {
        font-size: 20px;
    }
}

/* From the width the column headers appear at, the chrome can afford the room:
   the sheet opens on its headers, then an empty ruled row, then a bar three
   rows deep instead of two. All whole rows, so everything below still starts on
   a line. The breakpoint is the headers' own — below it there is no header row
   to open on, and a lone empty row above the bar would read as a stray gap. */
@media (min-width: 1280px) {
    :root {
        --bar: calc(3 * var(--paper-row));
    }

    /* An empty ruled row between the column headers and the bar. */
    .fx {
        margin-top: var(--paper-row);
    }

    /* The headers are a header, not a row of the sheet, so the count starts
       under them — as it does in a spreadsheet. */
    .rownums {
        top: var(--paper-row);
    }
}

@media (max-width: 1279px) {
    :root {
        --cols: 12;
        --content-start: 4;
        --content-span: 8;
        --side-cols: 2;
    }

    /* Twelve letters no longer fit over eight columns. */
    .sheet-cols {
        display: none;
    }
}

@media (max-width: 959px) {
    :root {
        --cols: 8;
        --content-start: 3;
        --content-span: 6;
        --side-cols: 1;
        --strip: 0px;
    }

    .rownums {
        display: none;
    }

    /* Two columns on a tablet and a phone, where four is most of the page.
       Same arithmetic as the four: the content is six columns here and four
       below 600, both even, so a two-column box centred in it is offset by a
       whole number of columns and the edges still land on the ruling. */
    #mark .cell {
        width: calc(100% * 2 / var(--content-span));
    }
}

@media (max-width: 599px) {
    :root {
        --cols: 4;
        --content-start: 2;
        --content-span: 4;
        --side-cols: 0;
    }
}

@media (max-width: 760px) {
    /* The 80% measure is there to stop centred type running ragged across a
       wide block. A phone's column is already narrow enough, and holding the
       line back from edges the title reaches only makes it look inset. */
    #hero .sub {
        max-width: 100%;
    }

    /* iOS Safari zooms the page in when a focused field is under 16px, and it
       never zooms back out. The field keeps the page's 14px everywhere else. */
    .textarea {
        font-size: 16px;
    }

    /* On a phone the player sits in the middle of the picture rather than in a
       corner, and takes more of it — a third of a phone-width picture would be
       too small to read as a placement. Centred by auto margins against a
       zeroed inset rather than by a translate, which leaves the transform free
       for the closing. */
    .player {
        width: 72%;
        inset: 0;
        margin: auto;
    }

    /* The player takes twice the share of the picture here, so the label has to
       climb with it — the desktop figure would leave it at two thirds size. */
    .player-ad {
        font-size: clamp(12px, 3.6vw, 22px);
    }

    /* The button is 23px here, which is a fine thing to look at and too small a
       thing to hit. The reach around it doubles without the button changing. */
    .player-close::after {
        content: "";
        position: absolute;
        inset: -50%;
    }

    /* Tables stack instead of scrolling sideways. A sheet scrolls horizontally
       on a phone, but a landing page must not — so the row becomes a block:
       its first cell is the heading, the rest read underneath. Cells that are
       already half-width stay paired, which is the two-column maximum. */
    .tbl {
        overflow: visible;
    }

    .trow {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        padding: 4px 0;
    }

    .tcell {
        grid-column: span 2;
        border-right: 0;
        padding: 6px 12px;
    }

    .tcell.c5 {
        grid-column: span 1;
    }

    /* Column captions have nothing to label once the row is stacked. */
    .trow-head {
        display: none;
    }

    .split {
        grid-template-columns: minmax(0, 1fr);
    }

    .title-hero {
        font-size: 32px;
        letter-spacing: -0.6px;
    }

    .title {
        font-size: 24px;
    }

    /* Half the field, at twice the size. Ninety-six characters across a phone
       is three and a half pixels each — a grey texture rather than a drawing.
       The box takes a taller shape and the SVG slices to fill it, so the same
       drawing shows forty-eight columns at seven pixels instead. */
    .art {
        aspect-ratio: 8 / 9;
    }

    /* The certification block gives its column back: a fraction of a phone set
       the caption at half the characters a line it wants. Centred copy needs no
       inset here — the screen is already the column.
       The auto margins go with the cap. An auto cross-axis margin stops a flex
       item stretching, so leaving them behind sized the title to its own text
       rather than to the block — centred either way, but not the full width
       this is meant to give back. */
    #mcm .title,
    #mcm .sub,
    #mcm .badge {
        max-width: none;
        margin-inline: 0;
    }
}
