/* The hero, phone build.

   A rewrite rather than another patch. The hero's mobile rules had grown
   across two sheets: a base written for a grid and a phone block that turned
   the same element into a flexbox, so the centring properties in one did
   nothing in the other and the clip sat left of centre in its own frame.

   This file owns the phone hero outright. It loads after every other sheet,
   so it wins on order without needing !important, and it states the whole
   layout in one place instead of correcting someone else's.

   The shape: a column with the clip centred in it and the copy beneath.
   Nothing is absolutely positioned except the wash behind everything, so
   there is no second coordinate system to keep in sync. */

@media (max-width: 760px) {

  /* The frame. Height comes from what is in it -- a fixed height is what put
     a portrait clip in a letterbox and a landscape one in a pillarbox. */
  .nst-hero,
  .nst-hero:has(.nst-hero__stage.is-portrait) {
    display: block;
    /* Square, and edge to edge. A rounded corner is what says "card": it draws
       the page's own colour back in over the film at all four corners, and the
       hero stops being the picture and becomes a tile of it. The clip runs to
       the sides instead.

       The margin goes with it -- the hero carried 16px each side, which is the
       gutter every other section keeps. On a section of text that margin is what
       makes it readable; on the one section that is a full-bleed picture it is a
       frame drawn around the film. The copy inside keeps its own gutter, so the
       type never touches the edge. */
    border-radius: 0;
    margin-left: 0;
    margin-right: 0;
    /* Room above the film.

       The hero sat directly under the Ask field, so the top of the picture met
       the dark chrome on a hard line -- and a bright frame against that line is
       the last thing that made the hero read as a panel dropped on the page. A
       gap of page ink above it means the film starts in open space instead of
       against an edge.

       28px on top of the 12 already there, so 40 in total -- up from 10. The fade
       sits inside the stage, so moving the hero moves the fade with it and gives
       back none of the frame; what the gap does is separate the dissolve from the
       chrome above it. At 22px the two ran together and read as one dark mass from
       the Ask field down into the picture. With 40 there is clearly page, then
       clearly film, and the fade is the join rather than the whole top of it. */
    margin-top: 28px;
    /* No overflow of its own -- the stage inside it clips the film, and a second
       box around that only adds another bottom edge to draw. Set further down
       with the rest of the container removal, so the two are not stated twice
       and differently. */
  }

  /* One column, centred. Stated for both clip states: is-portrait lands after
     the video reports its size, and anything that changes at that moment
     visibly jumps. */
  .nst-hero__stage,
  .nst-hero__stage.is-portrait,
  .nst-hero:has(.nst-hero__stage.is-portrait) .nst-hero__stage,
  .nst-hero:has(.nst-hero__stage.is-portrait) .nst-hero__stage.is-portrait {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;      /* the clip, horizontally */
    justify-content: flex-start;
    height: auto;
    min-height: 0;
    padding: 0 0 22px;
    border-radius: 0;
    /* No surface of its own. The clip covers the frame, so a background here is
       only ever visible as the edge around the film -- which is the thing that
       made the hero read as a panel sitting on the page instead of the picture
       being the page. The one moment it showed was before the poster loaded,
       and the body behind it is the same ink anyway. */
    background: transparent;
    overflow: hidden;
    /* Exactly the cap, whatever is inside it. The film is deliberately 2px
       taller than the frame so its own bottom edge falls outside the crop --
       but the stage is a flex column, so that overshoot grew it to 462 and
       carried the edge right back to the boundary. Fixing the height is what
       makes the overshoot actually overshoot. */
    height: var(--nst-cap);
    min-height: var(--nst-cap);
    max-height: var(--nst-cap);
  }

  /* The clip. width:auto with a capped height lets it keep its own shape --
     a 9:16 reel stays tall and a 16:9 clip stays wide, and neither is cropped
     to fill a frame it was not shot for. auto margins centre it whatever the
     flex container is doing. */
  .nst-hero__stage > video:not(.nst-hero__mirror),
  .nst-hero__stage > img:not(.nst-hero__mirror),
  .nst-hero__stage > iframe,
  .nst-hero__stage .nst-hero__video:not(.nst-hero__mirror),
  .nst-hero__stage .nst-hero__cover {
    position: relative !important;
    inset: auto !important;
    display: block;
    /* The frame, not the film's own shape.

       This used to size by `--nst-clip-w` and `--nst-ratio`, which is how a clip
       kept its own proportions. It fills the frame now, and the rule below says
       so -- but that made this one a liability rather than dead weight:
       --nst-clip-w is published by fitHero from a *measured* box, so on the
       first frame after a new clip mounts it still holds the last one's width.
       The film drew at that width, off to one side, and snapped to the frame a
       tick later. Tapping through up-next flashed left before centring.

       Stating the frame here as well means both rules agree from the first
       paint, and there is nothing to correct. */
    width: 100% !important;
    max-width: 100% !important;
    /* Same height as the fill rule below, so the two agree on the first paint as
       well as after it. */
    height: var(--nst-cap) !important;
    max-height: none !important;
    aspect-ratio: auto !important;
    margin: 0 !important;
    object-fit: cover !important;
    border-radius: 0;
    z-index: 1;
    /* Up out of the poster behind it rather than replacing it in one frame.
       The browser swaps a video's own poster for its first decoded frame
       instantly, and against a blurred wash of the same picture that reads as
       a jolt -- the clip appears to snap into place. Held clear until the
       frame is actually there, then faded over the top of it. */
    opacity: 0; transition: opacity .28s ease;
  }
  /* The clip fills the frame, whatever shape it was shot at.

     Sizing by width and ratio -- the rule above -- keeps a clip's own shape,
     and on a phone that means the frame changes size with every moment: a 9:16
     reel drew 206x314 with 94px of black either side, and a 1280x590 commercial
     drew 366x168, a strip across the top with the headline in the black
     underneath. One hero, two completely different objects.

     So the frame is fixed and the film is cropped into it. Every hero is the
     same box, edge to edge, and the page stops reflowing around whatever
     happened to be picked.

     What that costs, stated plainly: a reel keeps its full width and loses
     about half its height, ~163px off the top and the same off the bottom. A
     2.17 clip keeps its full height and a little over a third of its width. The
     crop is centred, which is where the subject usually is, but a play at the
     very top or the very edge of a frame can be lost. That is the trade for one
     shape.

     aspect-ratio has to be cleared explicitly. Given both a height and a ratio
     the ratio wins and the height is ignored, so the old sizing would survive
     this rule entirely.

     `.is-portrait` is spelled out on every selector, and that is not padding.
     The desktop sheet sizes a portrait clip with `width: auto; height: min(360px,
     100vh - 500px); object-fit: contain` and marks all of it !important -- it is
     right there and should stay, but it is outside any media query, so it
     applies on a phone too and its extra class beat this rule. A reel kept
     drawing 260x344 while every other shape filled the frame. Matching the
     class here is what makes the two agree. */
  .nst-hero__stage > video:not(.nst-hero__mirror),
  .nst-hero__stage > img:not(.nst-hero__mirror),
  .nst-hero__stage .nst-hero__video:not(.nst-hero__mirror),
  .nst-hero__stage .nst-hero__cover,
  .nst-hero__stage.is-portrait > video:not(.nst-hero__mirror),
  .nst-hero__stage.is-portrait > img:not(.nst-hero__mirror),
  .nst-hero__stage.is-portrait > iframe,
  .nst-hero__stage.is-portrait .nst-hero__video:not(.nst-hero__mirror),
  .nst-hero__stage.is-portrait .nst-hero__cover {
    width: var(--nst-frame-w) !important;
    max-width: 100% !important;
    /* Exactly the frame. This was `+ 2px` -- an attempt at the hairline, on the
       theory that overshooting put the film's bottom edge below the crop. It did
       not fix the hairline, and it forces iOS to clip a video layer that is
       larger than its container, which is a likely source of the rounded corners
       that appeared alongside it. Unverifiable from here either way, so it goes
       back to the honest value. */
    height: var(--nst-cap) !important;
    aspect-ratio: auto !important;
    object-fit: cover !important;
    /* The desktop portrait rule also carries `min-height: 240px` and
       `object-fit: contain`, both !important. A min-height above the cap would
       win over the height set here, and `contain` would letterbox inside the
       filled box -- putting the bands back inside the frame instead of beside
       it. Both are cleared rather than left to the cascade. */
    min-height: 0 !important;
    min-width: 0 !important;
    position: relative !important;
    inset: auto !important;
    margin: 0 auto !important;
    /* Square corners on the film itself.

       The portrait block in network-v2.css also carries `border-radius: var(--r)`
       -- 10px -- and its `.is-portrait` class outranked the plain `border-radius:
       0` further up this rule, so the clip kept rounded corners even after the
       hero and the rail lost theirs. On full-bleed film that shows as the page
       cutting into the picture at the top corners, right under the search bar. */
    border-radius: 0 !important;
    /* Clip the bottom row off the film itself.

       This is the hairline at the foot of the hero. On iOS a playing video is
       composited on its own GPU layer, and that layer's bottom edge is not cut
       by the stage's overflow:hidden the way a still image's is -- so the last
       row of the frame drew under the copy gradient and over the page. Full
       width, in whatever colour was at the bottom of that clip, and only ever
       while a clip was actually playing.

       Removing this rule brought the line straight back, which is the one thing
       about it that is confirmed rather than reasoned.

       What it is NOT responsible for is the rounded corners: those were
       `border-radius: var(--r)` from the portrait block above, and they survived
       this rule being deleted. The previous version of this also came with a 2px
       height overshoot on the video, which asked iOS to clip a layer larger than
       its container -- that pairing is gone, so the clip is exactly its frame and
       this only trims the bottom edge.

       Explicitly square corners in the inset(), so the clip region cannot pick up
       a radius from anywhere in the stack. */
    clip-path: inset(0 0 1px 0 round 0) !important;
  }

  /* A video waits for its first frame; everything else has nothing to wait
     for. Marking each mount separately means one that is missed never appears
     at all, so the rule is the other way round: only a clip is held back, and
     it holds itself back only until it can be seen. */
  .nst-hero__stage > img:not(.nst-hero__mirror),
  .nst-hero__stage > iframe,
  .nst-hero__stage .nst-hero__cover,
  .nst-hero__stage > video.is-ready:not(.nst-hero__mirror),
  .nst-hero__stage .nst-hero__video.is-ready:not(.nst-hero__mirror) { opacity: 1; }

  /* The blurred wash is gone.

     It existed to fill the bands beside a centred clip -- a portrait reel used
     to sit in the middle of the stage with flat black either side, and an
     overscaled blur of the poster was better than that. The clip covers the
     frame now, so there are no bands: the wash was a full-screen blurred layer
     with a video painted over every pixel of it, and blur(26px) on a
     viewport-sized box is not free on a phone.

     What it also did was cover the load, and the clip's own `poster` attribute
     already does that -- heroMedia sets it from moment.image, sharp and in
     place, so there is nothing to bridge.

     The layer is reused below for the fade at the top of the film. */

  /* The film fades into the header the way it fades into the copy.

     The bottom of the hero dissolves under the copy gradient, so the picture has
     no edge there -- but the top still began on a hard line under the search
     bar, which is the last thing making it read as a panel dropped on the page
     rather than the page itself. A short fade from the page's own ink does the
     same job at the top: bright footage stops butting against dark chrome, and
     the two meet in the middle instead.

     140px and opaque at the very top. A softer, shorter version was tried and
     went back -- at 100px starting at .82 the hard line came back into view, and
     the whole point of this layer is that the film has no edge where it meets the
     chrome. It costs the top of the frame, which is why the hero also drops
     further down the page: the gap above it is what keeps the top of the picture
     visible rather than the fade having to be gentle. */
  .nst-hero__stage.is-portrait::before,
  .nst-hero__stage::before {
    content: "";
    display: block;
    position: absolute;
    inset: 0 0 auto 0;
    height: 140px;
    /* Above the film, below the copy and the two corner columns.

       3, not 2. The clip's z-index here says 1, but the portrait block in
       network-v2.css:283 sets `z-index: 2` on the same element and wins -- so the
       fade at 2 tied with it, and a tie is settled by DOM order, which the video
       wins because it is prepended after. The fade was painting *under* the film
       and doing nothing at all: measured with the clip frozen, switching it off
       changed not one pixel.

       The copy and the two corner columns sit at 2 and 3 in this sheet and are
       later in the document, so they still draw over this. */
    z-index: 3;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      rgb(8, 8, 10) 0%,
      rgba(8, 8, 10, .92) 14%,
      rgba(8, 8, 10, .66) 38%,
      rgba(8, 8, 10, .30) 66%,
      rgba(8, 8, 10, 0) 100%);
  }

  /* No sharp second copy of the poster here.

     There was one: a ::after at `--nst-clip-w` and `--nst-ratio`, meant to
     stand in while the clip decoded. Those two variables are published by
     fitHero from a *measured* box, so they hold nothing until a video or
     iframe exists and has been laid out -- which is after the wait it was
     supposed to cover. So it drew at whatever the previous moment left behind:
     an upright strip in the middle of a wide black stage, with the headline
     printed across it, then a jump when the clip arrived and resized it.

     The stage already paints the poster full-frame on its own. Covering the
     load with a second differently-sized copy is what looked broken. */

  /* Safari's own start button, off.

     iOS draws a play badge in the dead centre of any video it has not started,
     regardless of `controls = false` -- that property governs the control bar,
     not this. The hero's centre is y=381 against a kicker at 376-402, so the
     badge landed squarely on the byline and read as a broken layout.

     It cannot be dodged by moving the copy: the badge is centred on the film and
     the copy owns the bottom half of it, so even a 200px block still catches its
     lower edge. And it is redundant -- the transport under the hero carries
     play, pause and sound, bound to this same element. So it goes.

     One rule per selector, deliberately. The pseudo-element's name differs by
     iOS version, and a selector list is thrown away whole if any part of it is
     unrecognised -- grouping these would mean the one spelling that works gets
     discarded along with the ones that do not. */
  /* Nothing here moves or hides the badge.

     Both were tried -- `display: none` on every spelling of the pseudo-element,
     then a transform to shift it up out of the byline. Neither is why the badge
     was there. It appears when iOS has refused to start the clip, so it is a
     symptom, and the fix was the autoplay itself: the hero is back on
     preload="auto", which is what iOS needs to actually begin playback. A clip
     that plays draws no badge. */
  .nst-hero__stage video::-webkit-media-controls-panel {
    display: none !important; opacity: 0 !important;
  }

  /* No mirror on a phone. This is the divider.

     The mirror is a blurred copy of the frame that filled the bands beside a
     centred clip. The film covers the whole frame now, so there is nothing for
     it to fill -- it sits behind an opaque picture, invisible, until it isn't:
     `transform: scale(1.15)` on a 460px box pushes it ~35px past the bottom, and
     once .nst-hero stopped clipping (which is what took the container edges off)
     that overhang landed in the gap above the up-next rail. A blurred, darkened,
     scaled-up copy of the clip, showing as a band the full width of the page.

     Measured: mirror top 117, bottom 646, against a stage that ends at 611.

     The JS half of this was already gated for video clips -- addMirror() returns
     early under 760px -- but fitHero mounts an <img> mirror on the poster path
     too, and that one was never covered. Hidden here rather than in the script,
     so both paths are handled by one rule. */
  .nst-hero__mirror { display: none !important; }

  /* The wash sits over the mirror and under everything else. */
  .nst-hero__scrim {
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
  }

  /* The copy, over the bottom of the film.

     It used to sit in the column beneath the clip. That gave the hero a hard
     edge across it -- the film stopped at one pixel and a flat slab of ink
     started at the next, with the headline on the slab -- and the two read as
     separate sections of the page rather than one card. The clip fills the frame
     now, so there is a picture to lay type over and no reason to stack them.

     The gradient is what makes it work: opaque at the bottom where the buttons
     are and clear at the top, so the copy has ground to stand on and the film
     still runs behind it uninterrupted. There is no seam because there is no
     boundary -- one frame, darkening toward the bottom.

     Absolute, so the stage is the clip's height and nothing else. That takes
     ~190px off the hero and lifts the rail underneath it.

     Spelled out for `.is-portrait` and stated !important for the same reason the
     sizing above is: two other sheets put this block back in the column.
     nilsportstech-network-v2.css:453 sets `position: relative` on
     `.is-portrait .nst-hero__body` outside any media query, and
     nilsportstech-network-v2-mobile.css:215 does the same on the bare class.
     Both are right where they were written and both outrank a plain selector
     here. */
  .nst-hero__body,
  .nst-hero__stage .nst-hero__body,
  .nst-hero__stage.is-portrait .nst-hero__body,
  .nst-hero__stage.is-wide .nst-hero__body {
    position: absolute !important;
    inset: auto 0 0 0 !important;
    left: 0 !important;
    right: 0 !important;
    /* Past the bottom, not flush to it. Sitting exactly on the stage's edge
       left the clip's last row of pixels to meet the page on a boundary that
       both sides round independently -- so a hairline of whatever was at the
       foot of the frame kept showing through, and it changed colour with the
       clip. Two pixels of overhang puts the opaque end of the gradient over
       that edge instead of beside it. The stage's overflow:hidden crops it, so
       nothing extends past the hero. */
    bottom: -2px !important;
    max-width: none !important;
    grid-row: auto !important;
    z-index: 2;
    width: auto !important;
    /* Room for the fade above the kicker, so type never starts inside the
       clear part of the gradient. Also !important: the two sheets above set
       their own padding on this block, and a 0-top would put the kicker in the
       clear part of the gradient with the film showing through it. */
    padding: 64px var(--gutter) 18px !important;
    display: flex;
    flex-direction: column;
    gap: 12px;
    /* Centred from the first frame, not from whenever the class lands.

       This is the left-then-centre jump. Alignment was only ever stated under
       `.is-portrait` (network-v2.css:453), and fitHero adds that class after the
       clip reports its dimensions -- so the copy first painted left-aligned
       against the default at :512, then snapped to centre a frame later. Every
       hero on a phone is centred, so it is stated here for all of them and there
       is no longer a state to switch between. */
    align-items: center !important;
    text-align: center !important;
    /* Fully opaque at the bottom, not 95%.

       At .95 the last rows of film showed through the gradient at five percent
       and met the solid page underneath -- which reads as a coloured hairline
       across the foot of the hero, taking whatever tone was at the bottom of
       that frame. It looked like a divider above the up-next rail, and it moved
       colour with the clip, which is what gave it away.

       The bottom stop is now the page's own ink at full strength, and it holds
       that for the first stretch so there is no gradient left to see through by
       the time the edge arrives. */
    background: linear-gradient(
      to top,
      rgb(8, 8, 10) 0%,
      rgb(8, 8, 10) 12%,
      rgba(8, 8, 10, .88) 38%,
      rgba(8, 8, 10, .62) 66%,
      rgba(8, 8, 10, 0) 100%);
  }
  /* The stage is the clip. With the copy over the film there is nothing below
     it to make room for, and the padding that held that room open now shows as
     a band of ink under the picture. */
  .nst-hero__stage,
  .nst-hero__stage.is-portrait,
  .nst-hero:has(.nst-hero__stage.is-portrait) .nst-hero__stage,
  .nst-hero:has(.nst-hero__stage.is-portrait) .nst-hero__stage.is-portrait {
    padding-bottom: 0;
  }
  /* The kicker sits highest in the copy, which is the thinnest part of the
     gradient, so it is the one line that can end up over open picture -- it was
     crossing a medal and a jersey and reading as grey on grey. A shadow rather
     than a heavier gradient: pushing the fade further up would start covering
     the subject to fix one small line. */
  .nst-hero__kicker {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 8px 10px; font-size: 10px;
    /* Two shadows: a tight one for edge definition and a wide soft one that
       acts as its own local scrim. One 4px shadow still left this grey on a
       sunlit field -- it is the line highest in the copy, so the gradient
       behind it is nearly clear. */
    text-shadow: 0 1px 2px rgba(0, 0, 0, .95), 0 0 14px rgba(0, 0, 0, .85);
  }
  .nst-hero__kicker .nst-rule { display: none; }
  .nst-hero__by { display: flex; align-items: center; gap: 8px; min-width: 0; }
  .nst-hero__title {
    font-size: clamp(28px, 8vw, 38px);
    line-height: 1.02;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* Two lines, not three. The caption is over the film now, and a three-line
     title with a three-line caption came to ~300px of a 460px clip -- most of
     the picture behind type. The headline is the claim and the caption is
     context; the caption is the one that can give up a line. */
  .nst-hero__caption {
    font-size: 15px; line-height: 1.45;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* The buttons centre from the first frame too. network-v2.css:460 centres them
     only under `.is-portrait`, so they were left-aligned until the class landed
     and slid across with the rest of the copy. */
  .nst-hero__actions,
  .nst-hero__stage.is-portrait .nst-hero__actions,
  .nst-hero__stage.is-wide .nst-hero__actions {
    display: flex; gap: 8px; flex-wrap: wrap;
    justify-content: center !important;
  }
  /* And the headline keeps one size. :458 gives a portrait clip
     `clamp(26px, 2.6vw, 36px)` against the clamp below -- on a 390px screen that
     is 26px versus 31px, so the title visibly grew as the class arrived. The
     mobile clamp wins in both states now. */
  .nst-hero__stage.is-portrait .nst-hero__title,
  .nst-hero__stage.is-wide .nst-hero__title {
    font-size: clamp(28px, 8vw, 38px) !important;
  }
  .nst-hero__actions .nst-btn { padding: 13px 20px; font-size: 13px; letter-spacing: .10em; }
  .nst-hero__actions .nst-btn--orange { padding: 13px 22px; font-size: 14px; }

  /* The two strips beside the clip.

     The clip is centred and keeps its own shape, so the space either side of
     it is (stage - clip) / 2. The old rules hard-coded a 203px clip and
     measured from that; --nst-strip derives the same figure from whatever
     width the clip actually came out at, which is what makes this hold for a
     reel and a game clip alike.

     Replies own the left strip and figures the right: the run grows as remarks
     arrive, and anything sharing that column gets pushed by every new line. */
  .nst-hero__stage {
    /* The cap the clip is sized against, and its real shape. fitHero() writes
       --nst-ratio (width / height) once the poster reports its dimensions;
       0.5625 is 9:16, the fallback for the moment before that lands.

       Height is the smaller of the cap and what the width allows, which is the
       same choice the clip itself makes -- so these strips track the clip
       rather than guessing at it. A 360px-tall clip was getting figures
       positioned for a 460px one, eighty pixels low. */
    --nst-ratio: 0.5625;
    --nst-cap: min(58vh, 460px);
    /* Off the viewport, not a percentage: percentages are not allowed inside a
       division, and the stage spans the screen less its gutters anyway. */
    /* The whole screen. This was the viewport less two gutters, matching the
       margin the hero used to carry; the hero runs edge to edge now, so taking
       the gutters off again would leave the clip 32px narrower than the stage
       it sits in -- a strip of page colour down both sides of the film. */
    --nst-frame-w: 100vw;
    --nst-clip-h: min(var(--nst-cap), calc(var(--nst-frame-w) / var(--nst-ratio)));
    --nst-clip-w: calc(var(--nst-clip-h) * var(--nst-ratio));
    --nst-strip: calc((100% - var(--nst-clip-w)) / 2 - 12px);
    /* The clip sits at the top of the column with the copy under it, so its
       middle is half its own height -- not half the stage, which includes the
       title, caption and buttons below. */
    --nst-clip: var(--nst-clip-h);
  }

  /* What a filled clip actually measures.

     These four used to be derived from --nst-ratio, because the clip's shape
     decided its box. It does not any more -- every clip takes the frame and the
     cap -- so they are stated rather than computed, or the replies and the
     figures would still be positioned for a shape nothing is drawn at.

     --nst-strip goes to zero: there are no bands beside a clip that spans the
     frame, so the two columns sit over the film, which is what they already do
     on a landscape clip. fitHero's publish() overwrites the two clip figures
     with the measured box a frame later, and agrees with these. */
  .nst-hero__stage,
  .nst-hero__stage.is-portrait,
  .nst-hero__stage.is-wide {
    --nst-clip-h: var(--nst-cap);
    --nst-clip-w: var(--nst-frame-w);
    --nst-strip: 0px;
    --nst-clip: var(--nst-cap);
  }

  /* Plays and likes: right strip, centred on the clip the way they were. */
  .nst-hero__counts,
  .nst-hero__stage:not(.is-portrait) .nst-hero__counts,
  .nst-hero__stage.is-portrait .nst-hero__counts {
    position: absolute; z-index: 3;
    left: auto; right: 8px; bottom: auto;
    /* Up out of the copy. These were centred on the clip, which is where the
       headline sits now that the copy is over the film -- "1.7K PLAYS" printed
       across the end of the title. The top corner is the part of the frame
       nothing else uses. */
    top: 18px;
    transform: none;
    width: auto;
    display: flex; flex-direction: column; align-items: flex-end;
    text-align: right; max-width: none; margin: 0;
    gap: 3px; font-size: 10px; pointer-events: auto;
    /* No container. A pill here read as a chip stuck on the film -- another
       edged box on a hero whose whole point is that nothing is boxed. The
       figures sit straight on the footage and carry their own contrast in the
       type instead: a tight shadow for definition and a wide soft one that acts
       as its own local shade, which is what holds them on a bright frame
       without drawing a shape. */
    padding: 0;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .95), 0 0 14px rgba(0, 0, 0, .9);
  }
  /* The gap separates each figure from its label -- a space between two flex
     items collapses away, and the line read "9.5KPLAYS". */
  .nst-hero__counts > span { display: flex; align-items: baseline; gap: .34em; }
  .nst-hero__counts[hidden] { display: none; }

  /* The replies drip in the left strip while the clip plays, so nothing is
     covered. overflow stays visible: the base rule clips it, and a long run
     then ate the figures hanging below. */
  /* No rule between the hero and what is up next.

     nilsportstech-network-v2-mobile.css:255 gives the up-next rail a
     `border-top`, which was right when the hero was a rounded card sitting in a
     gutter -- the line said where one ended and the next began. The hero is
     full-bleed film now and the rail runs under it, so that border reads as a
     divider drawn across the picture, right below the WATCH button. The change
     of content is the separation. */
  /* Nothing between the hero and what is up next.

     There were three separate edges here, which is why removing one at a time
     never finished the job:

       network-v2-mobile.css:255   .nst-upnext { border-top: 1px solid ... }
       network-v2.css:1438         the same, inside @media (max-width: 1180px)
                                   -- which a phone matches, and which sits
                                   later in the cascade than the first
       network-v2.css:227 / :524   overflow:hidden on both boxes, so each one
                                   draws its own hard bottom

     All of it made sense when the hero was a rounded card in a gutter and the
     rail was a panel beside it. The hero is full-bleed film now and the rail
     runs straight under it, so every one of those reads as a split across the
     page. The change of content is the separation. */
  .nst-upnext,
  .nst-hero .nst-upnext,
  aside.nst-upnext {
    border-top: 0 !important;
    border-left: 0 !important;
    border-bottom: 0 !important;
    background: transparent !important;
  }
  /* No box on either side of the join. overflow:hidden is what gives each one an
     edge to draw; with the film clipped to the frame there is nothing left that
     needs containing. */
  .nst-hero,
  .nst-hero:has(.nst-hero__stage.is-portrait),
  .nst-upnext {
    overflow: visible !important;
    border-radius: 0 !important;
  }

  /* Room between the film and the first card.

     The rail pads 12px, which put the cards' own top edges a hair under the
     hero -- and those edges are what looked like a divider: the playing card
     carries a 2px orange ring and the one beside it starts with a bright
     thumbnail, so at the foot of dark footage they read together as a rule
     across the page, inset by the rail's gutter and stopping short of the right
     edge. Not a border anywhere; just two card tops on the same line as the
     seam. Enough space and they read as cards again. */
  .nst-upnext [data-upnext] { padding-top: 22px; }

  /* The replies start on the plays' line and drip down from it.

     Both columns hang off the same top edge -- figures top right, remarks top
     left -- so the two read as one row of chrome across the top of the film
     rather than two things placed independently. Each new remark appears under
     the last, which is the direction the eye is already going.

     They stop before the gradient: 42% of the clip is the room above the copy,
     and a run that grew past it would print over the headline. The renderer
     keeps four on screen and drops the oldest off the top, so the column moves
     without ever needing more height than this. */
  .nst-hero__chat,
  .nst-hero__stage.is-portrait .nst-hero__chat,
  .nst-hero__stage.is-wide .nst-hero__chat {
    display: flex; position: absolute; z-index: 3;
    /* Column, stated. The base sheet lays this out as a row, which put the
       remarks side by side across the top of the frame instead of stacked. */
    flex-direction: column; align-items: flex-start;
    left: 8px; right: auto; bottom: auto;
    top: 18px; transform: none;
    width: auto; max-width: 64%;
    max-height: calc(var(--nst-clip-h) * .42); gap: 6px; overflow: hidden;
  }
  /* One place for these, whatever shape the clip is.

     These two used to move for a landscape clip: it filled the stage, so there
     was no side strip to sit in, and they were pushed to `bottom: 150px` and
     `bottom: 116px` -- up off the copy from underneath. Every clip fills the
     frame now, so "landscape" is not a different layout any more, and the only
     thing these rules did was put the figures halfway down the hero beside the
     caption on some moments and at the top on others.

     The top corners are the answer for both: replies top left, figures top
     right, on the same line, every time. The portrait rule above already says
     that, so these only have to stop contradicting it. */
  .nst-hero__stage:not(.is-portrait) .nst-hero__chat {
    top: 18px; bottom: auto; transform: none;
    left: 8px; right: auto;
    width: auto; max-width: 64%;
    align-items: flex-start; text-align: left;
  }
  .nst-hero__stage:not(.is-portrait) .nst-hero__counts {
    top: 18px; bottom: auto; transform: none;
    left: auto; right: 8px;
    width: auto; max-width: min(190px, 52%);
  }
  .nst-hero__chat[hidden] { display: none; }
  /* The face comes back. It was dropped because a 26px portrait and its gap ate
     a third of the column, but the importer stores one for every commenter and
     a rail of nameless remarks reads as thinner than the conversation is. At
     18px it costs a fifth of the column and the handle sits beside it. */
  /* Each remark on its own pill, for the same reason the figures got one: over
     open footage there is nothing behind these but the film, and bare white
     type on a sunlit field is unreadable. The pill is only as wide as the line,
     so the frame still shows around it. */
  .nst-hero__msg {
    grid-template-columns: 18px minmax(0, 1fr); gap: 2px 5px;
    /* Same as the figures: no box. Shadowed type instead, so the remarks read
       as part of the film rather than cards laid over it. */
    text-shadow: 0 1px 2px rgba(0, 0, 0, .95), 0 0 12px rgba(0, 0, 0, .9);
  }
  /* The base sheet fades the oldest two to .45 and .75 so the run dissolves at
     the top rather than clipping mid-word. Over open footage that is not depth,
     it is illegible -- there is no dark panel behind these to fade against. The
     oldest simply drops off the top instead, which the renderer already does at
     four on screen. */
  .nst-hero__msg:first-child,
  .nst-hero__msg:nth-child(2) { opacity: 1; }
  .nst-hero__msg-avatar {
    display: block; width: 18px; height: 18px; grid-row: span 2;
    border-radius: 50%; overflow: hidden;
  }
  .nst-hero__msg-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .nst-hero__msg-body { font-size: 10px; line-height: 1.3; }
  .nst-hero__msg-who { font-size: 8px; letter-spacing: .06em; }
}
