/* NILSportsTech home, dark editorial build.

   Ported from the desktop design comp. The comp is a fixed 1440px artboard with
   every rule inline; this keeps its type, colour and rhythm but lets the grids
   reflow, since the same page has to survive a laptop and a phone. */

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

:root {
  --ink: #08080a;
  --panel: #0b0b0e;
  --card: #0e0e12;
  --line: #1c1c20;
  --line-strong: #26262c;
  --text: #f4f4f2;
  --muted: #a4a4ac;
  --dim: #6d6d76;
  --faint: #4a4a52;
  /* Named so a light theme can restate them. These were literals repeated
     across the sheet -- control edges, small-caps labels, inset wells -- and
     a literal is a value no theme can reach. */
  --edge: #2a2a30;
  --edge-strong: #2f2f36;
  --label: #8b8b93;
  --label-dim: #5c5c64;
  --well: #0a0a0a;
  --well-raised: #101014;
  --rise: #5ee6a8;
  --orange: #ff4a15;
  --mono: 'JetBrains Mono', ui-monospace, monospace;
  --cond: 'Barlow Condensed', sans-serif;
  --gutter: 32px;
  /* Panes. A surface is lit along its top edge and dropped onto the page rather
     than being a flat grey rectangle -- that highlight and that shadow are what
     give it an edge, and they work over flat black, which the page is. Two
     weights: a pane carrying content sits lighter than one leading a section.

     No backdrop blur. There is nothing behind these worth picking up, and it
     would cost a compositing layer each to return the tint stated here. */
  --pane: #14141a;
  --pane-lead: #191920;
  --pane-edge: inset 0 1.5px 0 rgba(255,255,255,.10),
               inset 0 0 0 1px rgba(255,255,255,.055),
               0 14px 44px rgba(0,0,0,.45);
  /* Hairlines read as light between panes, not as dark seams between boxes. */
  --line-glass: rgba(255,255,255,.12);
  --line-glass-strong: rgba(255,255,255,.20);
  /* The comp's placeholder hatch, kept for media that has not loaded yet. */
  --hatch: repeating-linear-gradient(115deg, #1b1b22 0 10px, #15151b 10px 20px);
  /* Square everywhere plus a 2px gutter welded the photo grids into one slab:
     with no corner and no gap you read a wall of film, not six moments. A small
     radius gives each tile an edge of its own without softening the page --
     it is a seam, not a rounded card. */
  --r: 10px;
  --r-sm: 6px;
  /* The pill survives the flattening. It is doing different work: it marks the
     things you can act on -- the search field, the two header actions, the
     filter chips -- against a page of square surfaces you only read. */
  --r-pill: 999px;
}

body { margin: 0; background: var(--ink); color: var(--text); font-family: 'Barlow', sans-serif; }
a { color: inherit; text-decoration: none; }
a:hover { color: var(--orange); }
img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Cards hold the hatch until their image decodes, then cross-fade to it. Flat
   black behind an undecoded image reads as a broken card. */
.nst-card img, .nst-reel img, .nst-offerrow__thumb img, .nst-upnext__thumb img,
.nst-biggest img, .nst-watched__lead img, .nst-watched__tile img, .nst-short img {
  opacity: 0; transition: opacity .35s ease;
}
.nst-card img.is-loaded, .nst-reel img.is-loaded, .nst-offerrow__thumb img.is-loaded,
.nst-upnext__thumb img.is-loaded, .nst-biggest img.is-loaded,
.nst-watched__lead img.is-loaded, .nst-watched__tile img.is-loaded,
.nst-short img.is-loaded { opacity: 1; }
/* Full strength. The half-opacity was for a layout where the copy sat over
   this picture and it had to drop back to let the type read. It sits beside
   the copy now -- title at x:202, picture at x:1038, no overlap -- so the fade
   was doing nothing but washing out the one photograph in the section. */
.nst-biggest img.is-loaded { opacity: 1; }
.nst-offer:not(.nst-offer--lead) img.is-loaded { opacity: .34; }

/* Tall enough to reach the bottom of the screen, with the footer pushed to it.

   A view with little in it -- Watch on a filter that matches three clips, a
   directory with one row -- left the footer floating mid-screen and 260px of
   nothing under it, because the wrapper only ever stood as tall as its
   content. svh rather than vh: on iOS vh is the viewport with the browser
   bars retracted, so 100vh is taller than the screen actually shows and the
   footer lands below the fold on a page that has no scrolling to do. */
.nst {
  max-width: 1440px; margin: 0 auto; overflow-x: hidden;
  min-height: 100svh; display: flex; flex-direction: column;
}
.nst > .nst-foot { margin-top: auto; }

/* --- ticker --------------------------------------------------------------- */

@keyframes nst-tick { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes nst-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* Edge to edge, whatever the window.

   The shell caps at 1440px so the page holds a readable measure, and the
   ticker was being capped with it -- on a wide screen it stopped 280px short
   of each edge and sat as a bar with the page behind it showing either side.
   It is a band across the top, so it breaks out of the shell and takes the
   whole viewport while everything below stays contained. */
.nst-ticker {
  height: 36px; background: var(--orange); color: var(--well);
  display: flex; align-items: center; overflow: hidden; white-space: nowrap;
  /* Edge to edge, out of the shell's clip.

     The shell caps the page at 1440px so it holds a readable measure, and it
     hides its overflow -- so widening this to 100vw was not enough on its own:
     the box reached the window but the paint stopped at 1440 and the page
     showed through at both ends as a gap.

     Fixed to the viewport instead of stretched inside the parent, so the clip
     has nothing to cut. The band leads the page and nothing scrolls past it,
     so taking it out of the flow costs nothing but the height, which the shell
     pads back below. */
  /* Absolute, not fixed. Fixed took it out of the flow so it cleared the
     shell's clip, but it also pinned the band to the window -- it followed the
     scroll down the whole page, which is not what a headline strip is for. It
     leads the page and then it leaves.

     Absolute against the shell keeps it out of the clip in the same way and
     lets it scroll away with everything else. */
  position: absolute; top: 0; left: 0; right: 0; z-index: 60;
  width: 100vw;
}
/* The shell is the frame this is positioned against, and it makes room for a
   band that no longer takes any of its own. */
.nst { position: relative; padding-top: 36px; }
/* Duration is set from the run's own width in JS, at a fixed pixels-per-second,
   so a busy week and a quiet one scroll at the same readable pace. The 40s here
   is only what a run uses before that lands. */
.nst-ticker__run { display: flex; gap: 36px; padding-left: 28px; font-family: var(--cond); font-size: 15px; font-weight: 600; letter-spacing: .12em; text-transform: uppercase; animation: nst-tick var(--tick-duration, 40s) linear infinite; }
.nst-ticker__run:empty { animation: none; }

/* --- header --------------------------------------------------------------- */

.nst-head { display: grid; grid-template-columns: 260px 1fr auto; align-items: center; gap: 28px; padding: 22px var(--gutter); }
.nst-brand { display: flex; align-items: center; gap: 12px; }
/* The mark from /nilsportstech-network: a flat orange circle, no gradient and
   no glow. Kept at that page's 36px -- the square it replaces was 30px, but the
   circle needs the extra to read as the same logo rather than a shrunk copy. */
.nst-brand__mark {
  width: 36px; height: 36px; flex-shrink: 0;
  border-radius: 50%;
  background: var(--orange);
  color: var(--ink);
  display: grid; place-items: center;
  font-family: var(--cond); font-weight: 900; font-size: 20px;
}
/* The wordmark and its line stack beside the mark, so the circle stays centred
   against both rather than against the first line only. */
.nst-brand__text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.nst-brand__word { font-family: var(--cond); font-size: 23px; font-weight: 700; letter-spacing: .14em; line-height: 1; }
/* Quiet on purpose: it names what the network is, and a second line at the
   wordmark's weight would read as two logos. */
.nst-brand__tag { font-family: var(--mono); font-size: 9px; letter-spacing: .18em; color: var(--faint); text-transform: uppercase; white-space: nowrap; }

/* A pill. The extra side padding is what makes the shape work -- against a
   rounded end, text set at the old 20px reads as if it is falling out of the
   curve. */
.nst-ask { display: flex; align-items: center; gap: 14px; background: var(--well-raised); border: 1px solid var(--edge); padding: 14px 24px; min-width: 0;  border-radius: var(--r-pill); transition: border-color .16s ease, background .16s ease; }
.nst-ask:hover { border-color: var(--edge-strong); }
/* A glyph now, not a shape: the box holds ✦ rather than being painted into a
   disc. No pulse -- the star carries the meaning on its own, and a blinking one
   reads as an unread badge sitting in the search field. */
.nst-ask__dot { flex-shrink: 0; color: var(--orange); font-size: 17px; line-height: 1; }
.nst-ask__label { font-family: var(--cond); font-size: 15px; letter-spacing: .18em; color: var(--orange); font-weight: 600; flex-shrink: 0; }
.nst-ask__hint { color: var(--label-dim); font-size: 16px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The box is a real field now -- it takes a question and hands it to the Ask
   page rather than sitting there as a label. */
.nst-ask { cursor: text; }
.nst-ask:focus-within { border-color: var(--orange); }
.nst-ask__input { flex: 1; min-width: 0; background: none; border: 0; padding: 0; color: var(--text); font-family: inherit; font-size: 16px; }
.nst-ask__input::placeholder { color: var(--label-dim); }
.nst-ask__input:focus { outline: none; }
/* Rounded to match the field it sits inside -- a hard-cornered chip in a pill
   was the one square left in the header. */
.nst-ask__key { margin-left: auto; font-family: var(--mono); font-size: 11px; color: var(--faint); border: 1px solid var(--edge); padding: 4px 9px; flex-shrink: 0; border-radius: var(--r-pill); }

.nst-head__actions { display: flex; justify-content: flex-end; gap: 10px; }
/* Transport glyphs stay type, not emoji.

   ▶ and ⏸ are ordinary Unicode symbols, but iOS promotes them to Apple Color
   Emoji by default -- the WATCH button shipped a blue-and-white cartoon glyph
   in the middle of an orange pill, and the bar's controls were colour stickers.
   Naming a font stack with no emoji family in it makes the browser fall back to
   the monochrome glyph in a text font.

   The markup pairs this with U+FE0E, the text-presentation selector; the
   font-family is the half that works when a selector is missing. */
.nst-playing__btn, .nst-transport__btn, .nst-reel__play,
.nst-watchtile__play, .nst-btn--orange {
  font-family: var(--cond), 'Helvetica Neue', Arial, sans-serif;
  font-variant-emoji: text;
}

/* These are anchors now. inline-flex so the two sit at one height and the +
   centres against the label, rather than sitting on the text baseline. */
.nst-btn { display: inline-flex; align-items: center; gap: 7px; font-family: var(--cond); font-size: 14px; letter-spacing: .14em; padding: 13px 24px; white-space: nowrap; cursor: pointer;  border-radius: var(--r-pill); transition: border-color .16s ease, background .16s ease, transform .16s ease; }
.nst-btn--ghost { border: 1px solid var(--edge-strong); }
.nst-btn--ghost:hover { border-color: var(--orange); background: #16161b; }
.nst-btn--light { background: var(--text); color: var(--ink); font-weight: 600; }
/* Only the primary lifts. Both moving would read as the whole header twitching
   under the pointer rather than one thing answering it. */
.nst-btn--light:hover { transform: translateY(-1px); }
.nst-btn--light:active { transform: translateY(0); }
/* The page turns links orange on hover; on the light button that is dark text
   going orange against near-white, so it keeps its own colour and lifts the
   background instead. */
.nst-btn--light:hover { color: var(--ink); background: #fff; }
.nst-btn--light b { font-size: 17px; font-weight: 700; line-height: 0; }
.nst-btn--orange { background: var(--orange); color: var(--well); font-weight: 600; padding: 15px 28px; letter-spacing: .16em; font-size: 15px; }
.nst-btn--orange:hover { transform: translateY(-1px); box-shadow: 0 10px 24px rgba(255,74,21,.28); }
.nst-btn--orange:active { transform: translateY(0); box-shadow: none; }

/* A ghost button standing on artwork rather than flat page. Over a photo an
   outline with nothing behind it reads as a caption -- the fill and the blur
   give it a surface, so it looks like the control it is next to. */
.nst-btn--onart {
  padding: 15px 28px; letter-spacing: .16em; font-size: 15px; font-weight: 600;
  border-color: rgba(255, 255, 255, .34);
  background: rgba(18, 18, 22, .55);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.nst-btn--onart:hover {
  border-color: #fff; background: rgba(28, 28, 34, .78);
  transform: translateY(-1px); color: var(--text);
}
.nst-btn--onart:active { transform: translateY(0); }
/* No destination yet -- say so rather than inviting the click. */
.nst-btn--onart[aria-disabled="true"] { opacity: .5; pointer-events: none; }

/* No rule under the tabs: the player and the rail below already draw their own
   top edges, so it was a third line in the same inch of the page. */
.nst-nav { display: flex; align-items: center; gap: 30px; padding: 0 var(--gutter) 18px; font-family: var(--cond); font-size: 15px; letter-spacing: .16em; text-transform: uppercase; color: var(--label); }





.nst-nav .is-active { color: var(--orange); font-weight: 600; border-bottom: 2px solid var(--orange); padding-bottom: 6px; }
/* The rail's title, on the tab row and over the rail it names. Same width as
   the rail so the two line up down the page. */
.nst-nav__rail { margin-left: auto; width: 320px; display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-family: var(--mono); font-size: 11px; letter-spacing: .2em; color: var(--orange); }
.nst-nav__rail[hidden] { display: none; }
.nst-nav__rail > span:last-child { color: var(--dim); letter-spacing: 0; }
/* The phone drawer's control. Present in the markup at every width, shown only
   by the mobile sheet -- on a wide screen the nav is the row itself. */
.nst-nav__burger { display: none; }
.nst-nav__meta { margin-left: auto; font-family: var(--mono); font-size: 11px; letter-spacing: .16em; color: var(--dim); text-transform: none; }

/* --- hero ----------------------------------------------------------------- */

/* An explicit row height. Left to auto the row sized to the taller column --
   the queue -- and the aside's height:100% then resolved against that, so the
   rail grew instead of scrolling. */
/* Hero and its transport share one screen. At 700px plus a 74px bar the
   controls fell below the fold, which is the one thing on this page that has
   to be reachable without scrolling. */
/* The whole player, transport bar included, without scrolling. --nst-above is
   what the ticker, header, nav and that bar actually measure at runtime, so
   the row takes exactly what is left rather than a number typed here that
   drifts the moment one of them changes. */
/* Inset to the gutter, so the panel's left edge lines up with the logo and nav
   above it rather than running flush to the viewport while everything else on
   the page starts 32px in. */
.nst-hero { display: grid; grid-template-columns: 1fr 320px; grid-template-rows: min(760px, calc(100vh - var(--nst-above, 210px))); align-items: stretch; overflow: hidden; margin: 0 var(--gutter); border-radius: var(--r); }
/* The stage is a grid item, so it has to be told it may shrink -- left at auto
   its min-content floor kept the overscaled crop at full size and the video
   spilled 190px past the hero, straight over the transport bar. */
.nst-hero > * { min-width: 0; min-height: 0; }
.nst-hero:has(.is-portrait) { grid-template-rows: minmax(0, min(760px, calc(100vh - var(--nst-above, 210px)))); }
/* Fill the row rather than assert a height. A fixed 700px outgrew the row once
   the hero was capped to keep the transport above the fold, so the media hung
   past the bottom and painted over the transport. */
.nst-hero__stage { position: relative; height: 100%; background: var(--hatch); overflow: hidden; border-radius: var(--r); }
.nst-hero__stage img, .nst-hero__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border: 0; }
/* A 16:9 frame in a 700px-tall stage letterboxes, so scale it to cover. The
   ratio is against the stage, not the viewport -- sizing off vh let the frame
   overflow the hero and paint over the page. */
.nst-hero__video--frame {
  /* Overscale so the frame covers the stage. YouTube's chrome hugs the frame
     edges, so cropping the sides pushes the title bar, watermark and "More
     videos" rail outside the visible area rather than onto the page. */
  top: 50% !important; left: 50% !important; right: auto !important; bottom: auto !important;
  width: 132%; height: 132%; min-width: 100%; min-height: 100%;
  transform: translate(-50%, -50%); pointer-events: none;
}

/* Portrait hero. A 9:16 clip pinned beside the copy leaves most of a 700px
   landscape stage empty, so a vertical moment shortens the whole hero instead:
   the clip sits centred at its own ratio and the copy moves underneath it.
   Nothing is cropped and there is no dead space to fill. */
.nst-hero:has(.nst-hero__stage.is-portrait) { grid-template-columns: 1fr 320px; }

/* A centred 9:16 clip leaves a 1140px stage mostly empty, so the portrait hero
   narrows to the clip and sets the copy directly beneath it. */
.nst-hero__stage.is-portrait { height: 100%; min-height: 0; background: var(--ink); display: grid; grid-template-rows: auto auto; justify-items: center; align-content: center; padding: 16px 0 18px; }
.nst-hero__stage.is-portrait .nst-hero__video:not(.nst-hero__mirror),
.nst-hero__stage.is-portrait .nst-hero__cover,
.nst-hero__stage.is-portrait > img:not(.nst-hero__mirror),
.nst-hero__stage.is-portrait > video:not(.nst-hero__mirror),
.nst-hero__stage.is-portrait > iframe {
  /* Beats the landscape cover-crop, which pins its sides with !important. The
     mirror is excluded: it carries the same class but has to stay absolute so
     it can wash the whole stage rather than joining the grid. */
  position: relative !important; inset: auto !important;
  /* Sized so the whole stack fits the row. The clip, title, caption and buttons
     came to ~648px against a 620px row, and the overflow landed on the buttons
     -- WATCH was sliced in half by the stage's own overflow:hidden. The clip
     gives up the difference; it is the one part with height to spare. */
  width: auto !important; height: min(360px, calc(100vh - 500px)) !important;
  /* A floor, so a very short window still shows a real clip rather than a
     sliver -- the stage scrolls at that point rather than the buttons being
     cut, which is the failure this whole rule exists to prevent. */
  min-height: 240px !important;
  min-width: 0 !important; max-width: 100% !important;
  margin: 0 auto !important;
  /* No forced ratio and no crop. Locking the box to 9:16 and covering it trimmed
     the sides of anything that was not exactly that shape -- a scorebug lost its
     first two characters. Height is fixed, width follows the clip. */
  transform: none !important; object-fit: contain;
  border-radius: var(--r); z-index: 2; grid-row: 1; justify-self: center;
}
/* A portrait clip fills the frame like a landscape one does.

   It used to be centred at its own ratio -- capped at 360px so the title and
   buttons stacked under it still fit the row -- which left a 203px clip in a
   1056px stage with darkness down both sides. Covered, the hero is one picture
   with the copy on it, which is what a landscape clip already looked like and
   the better of the two.

   The blurred mirror goes with the cap: it existed to fill the space either
   side, and there is no space either side any more. */
@media (min-width: 901px) {
  .nst-hero:has(.nst-hero__stage.is-portrait) { grid-template-columns: 1fr 320px; }
  .nst-hero__stage.is-portrait {
    display: block; padding: 0; background: var(--hatch);
  }
  .nst-hero__stage.is-portrait .nst-hero__video:not(.nst-hero__mirror),
  .nst-hero__stage.is-portrait .nst-hero__cover,
  .nst-hero__stage.is-portrait > img:not(.nst-hero__mirror),
  .nst-hero__stage.is-portrait > video:not(.nst-hero__mirror),
  .nst-hero__stage.is-portrait > iframe {
    position: absolute !important; inset: 0 !important;
    width: 100% !important; height: 100% !important;
    max-height: none !important; min-height: 0 !important;
    object-fit: cover !important;
    /* Under the wash. The stacked portrait rules lifted the clip to z-index 2
       so it sat above the scrim -- which was right while the copy was below
       the picture and the scrim was off. Covered, the copy is on the picture
       and the wash has to be between them, or a title lands on a lit graphic
       and disappears into it. */
    z-index: 0 !important;
  }
  .nst-hero__stage.is-portrait .nst-hero__mirror { display: none !important; }
}

/* --- hero chat ------------------------------------------------------------

   Comments from the post, arriving one at a time while the clip plays. A
   portrait clip leaves ~450px of empty black to its left; the chat lives there
   rather than over the footage, so it never covers the play. No panel and no
   border -- floating text reads as an overlay, a bordered box reads as another
   sidebar, and the hero already has one of those on the right. */
.nst-hero__chat {
  position: absolute; left: var(--gutter); bottom: 96px; z-index: 3;
  /* Stops short of the copy. The title block is a 620px column centred in the
     stage, so a chat sized off the stage alone ran underneath it and the word
     HIGHLIGHTS printed straight through a comment. This is the gap to the left
     of that column, less the gutter and a margin, so the two never meet. */
  width: min(300px, calc((100% - 620px) / 2 - var(--gutter) - 24px));
  display: flex; flex-direction: column; justify-content: flex-end;
  gap: 10px; max-height: 46%; overflow: hidden; pointer-events: none;
}
/* Under ~1200px the copy column leaves no room beside it; the chat would be a
   sliver of wrapped single words, so it steps aside entirely. */
@media (max-width: 1200px) {
  .nst-hero__chat, .nst-hero__counts { display: none; }
}
.nst-hero__chat[hidden] { display: none; }
/* A portrait reel leaves black to its left and the chat sits in it. A landscape
   clip fills the stage, so there is no dead column -- putting the chat over the
   footage's bottom-left printed comments across the athlete's face. It goes to
   the right instead, above the copy's own column, which is the one edge a
   filling video can spare: the title block is centred, so the gap to its right
   is as wide as the gap to its left and carries nothing. */
/* Both kinds of clip, on a wide screen. The exclusion was written when a
   portrait reel left a black column to its left for the chat to sit in -- it
   covers the stage there now, so there is no such column and the remarks
   belong on the right of both.

   Only there. A phone still stacks a reel as a narrow column with that strip
   beside it, and the phone sheet puts the remarks in it -- left unscoped this
   dragged them back over the clip and the figures printed through them. */
@media (min-width: 901px) {
  .nst-hero__stage .nst-hero__chat {
    left: auto; right: var(--gutter); bottom: 130px;
    align-items: flex-end; text-align: right;
  }
  .nst-hero__stage.is-portrait .nst-hero__counts {
    left: auto; right: var(--gutter); justify-content: flex-end;
  }
}
.nst-hero__stage:not(.is-portrait) .nst-hero__chat {
  left: auto; right: var(--gutter); bottom: 130px;
  align-items: flex-end; text-align: right;
}
.nst-hero__stage:not(.is-portrait) .nst-hero__counts {
  left: auto; right: var(--gutter); justify-content: flex-end;
  /* The cap comes off. It is sized for the chat, which is a column and stacks
     inside 300px happily; the figures are a single row, and "2.5K plays" beside
     "465 likes" does not fit that width -- the row wrapped, the numbers spilled
     out of the box and the two labels were left sitting alone over the buttons.
     There is nothing to guard against here anyway: the cap exists to keep the
     block clear of the centred copy column, and on this edge it already is. */
  max-width: none; white-space: nowrap;
  /* Under the replies rather than level with them, so the two read as one group
     on the right instead of a row that collides with the chat's last line. */
  bottom: 96px;
}
/* The rows read right-to-left with the mark on the outside, so the ragged edge
   faces the copy rather than the frame. */
.nst-hero__stage:not(.is-portrait) .nst-hero__msg {
  grid-template-columns: minmax(0, 1fr) 26px;
}
.nst-hero__stage:not(.is-portrait) .nst-hero__msg-avatar { order: 2; }

.nst-hero__msg {
  display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 9px; align-items: start;
  /* Rises into place as it arrives, the way a chat line lands. */
  animation: nst-chat-in .45s cubic-bezier(.22,.61,.36,1) both;
}
/* The run fades at the top rather than clipping mid-word. */
.nst-hero__msg:first-child { opacity: .45; }
.nst-hero__msg:nth-child(2) { opacity: .75; }

.nst-hero__msg-avatar {
  width: 26px; height: 26px; border-radius: 50%; overflow: hidden; background: var(--hatch);
  display: grid; place-items: center; font-family: var(--mono); font-size: 10px;
  color: var(--muted); text-transform: uppercase;
}
/* Static, not absolute. The chat sits inside the stage, so `.nst-hero__stage
   img` reached these too and pulled each commenter's picture out of its 26px
   circle -- a row of 162px bands down the side of the video. */
.nst-hero__msg-avatar img { position: static; inset: auto; width: 100%; height: 100%; object-fit: cover; }
.nst-hero__msg-body { min-width: 0; font-size: 13px; line-height: 1.4; }
.nst-hero__msg-who {
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em; color: var(--orange);
}
.nst-hero__msg-text {
  color: var(--text); text-shadow: 0 1px 12px rgba(0,0,0,.9);
  overflow-wrap: anywhere;
}

/* Plays and likes sit under the chat, where a stream puts its viewer count.
   Tabular figures so the numbers do not jitter as they tick. */
.nst-hero__counts {
  /* Level with WATCH and the app button, not on a line of its own.

     The copy block sits at bottom:72 with the buttons last inside it, and this
     was placed independently at 60 -- so the figure landed 54px below the row
     it belongs to and read as a stray line under the hero. Same baseline as
     the buttons now: it is a fact about the clip, and it belongs beside the
     things you can do with it. */
  position: absolute; left: var(--gutter); bottom: 78px; z-index: 3;
  max-width: min(300px, calc((100% - 620px) / 2 - var(--gutter) - 24px));
  display: flex; align-items: center; gap: 14px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .12em;
  color: var(--muted); text-transform: uppercase; pointer-events: none;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 12px rgba(0,0,0,.9);
}
.nst-hero__counts[hidden] { display: none; }
.nst-hero__counts b { color: var(--text); font-weight: 600; }
.nst-hero__counts .nst-hero__dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--orange);
  display: inline-block; margin-right: 7px;
  animation: nst-live-pulse 2s ease-in-out infinite;
}
@keyframes nst-live-pulse {
  0%, 100% { opacity: 1; } 50% { opacity: .35; }
}
@media (prefers-reduced-motion: reduce) {
  .nst-hero__counts .nst-hero__dot { animation: none; }
}

@keyframes nst-chat-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .nst-hero__msg { animation: none; }
}

/* Copy sits under the clip, centred, rather than floating over dead space. */
/* Centred under the clip, which is where it belongs while the clip is a narrow
   column with room beneath it. The wide screen covers the stage instead and
   moves this onto the picture -- see the 901px block above. */
.nst-hero__stage.is-portrait .nst-hero__body {
  position: relative; inset: auto; left: auto; bottom: auto; width: auto;
  grid-row: 2; align-items: center; text-align: center;
  padding: 18px var(--gutter) 0; gap: 10px; max-width: 620px;
}
.nst-hero__stage.is-portrait .nst-hero__title { font-size: clamp(26px, 2.6vw, 36px); }
.nst-hero__stage.is-portrait .nst-hero__caption { max-width: 560px; font-size: 15px; line-height: 1.45; }
.nst-hero__stage.is-portrait .nst-hero__actions { justify-content: center; }
/* Off while the copy sits below the clip: there is nothing to darken. The wide
   screen turns it back on, where the copy is on the picture. */
.nst-hero__stage.is-portrait .nst-hero__scrim { display: none; }

/* The wide screen's portrait hero, after the stacked rules it overrides.

   The clip covers the stage there, so the copy goes on the picture and the
   wash comes back with it -- both of which are wrong on a phone, where the
   clip is a column with room beneath it and the copy belongs under it. */
@media (min-width: 901px) {
  .nst-hero__stage.is-portrait .nst-hero__body {
    position: absolute; inset: auto; left: var(--gutter); bottom: 72px;
    width: min(720px, calc(100% - var(--gutter) * 2)); max-width: none;
    grid-row: auto; align-items: flex-start; text-align: left;
    padding: 0; gap: 22px;
  }
  .nst-hero__stage.is-portrait .nst-hero__actions { justify-content: flex-start; }
  .nst-hero__stage.is-portrait .nst-hero__scrim { display: block; }
}
/* Copy is below the clip rather than over it, so there is nothing to reveal by
   fading it -- the rest state only makes sense when text sits on the video. */
/* Tucked into the clip's bottom-right corner. The clip is centred and its width
   is 9/16 of its height, so half that width offsets from centre. */
/* The clip no longer has a fixed ratio, so the button cannot be offset from
   centre by a computed width -- it sits over the clip's lower edge instead. */
/* The wash tints the surround so the clip is not floating on flat black. */
.nst-hero__mirror { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; object-fit: cover; filter: blur(70px) saturate(1.6) brightness(.7); opacity: .55; transform: scale(1.25); }

/* The poster sits over the frame until playback settles, so none of YouTube's
   loading chrome flashes before the crop takes effect. */
.nst-hero__cover { position: absolute; inset: 0; z-index: 2; object-fit: cover; transition: opacity .5s ease; }
.nst-hero__cover.is-gone { opacity: 0; pointer-events: none; }
/* Two light gradients rather than one heavy horizontal wash. The old ramp hit
   full black at 6% and painted a solid band down the left of every frame; this
   darkens only where the copy actually sits. */
.nst-hero__scrim {
  position: absolute; inset: 0; z-index: 1;
  background:
    linear-gradient(90deg, rgba(8,8,10,.82) 0%, rgba(8,8,10,.45) 34%, rgba(8,8,10,0) 62%),
    linear-gradient(0deg, rgba(8,8,10,.78) 0%, rgba(8,8,10,.2) 34%, rgba(8,8,10,0) 58%);
}
/* Once the moment has played a few seconds the copy recedes the way a streaming
   front page does: the headline shrinks back and the supporting lines clear out,
   leaving the frame. Any attention -- pointer, keyboard, a screen reader --
   brings the whole block straight back. */
/* The copy held one size and one position. It used to shrink five seconds into
   a clip and spring back on hover, which read as the page rearranging itself
   under the pointer -- the transitions that drove it are gone with it. */
/* Aligned to the gutter, so the kicker, headline and buttons start on the same
   line as the logo and nav above them -- 56px left the hero's copy sitting a
   little inside everything else on the page. */
.nst-hero__body { z-index: 3; position: absolute; left: var(--gutter); bottom: 72px; width: min(720px, calc(100% - var(--gutter) * 2)); display: flex; flex-direction: column; gap: 22px; }
.nst-hero__kicker { display: flex; align-items: center; gap: 14px; font-family: var(--mono); font-size: 11px; letter-spacing: .2em; color: var(--orange); text-transform: uppercase; }
.nst-hero__kicker > span:last-child { color: var(--label); }
.nst-rule { width: 56px; height: 1px; background: var(--edge-strong); flex-shrink: 0; }
/* The comp's 96px sat under a two-word headline. Real ones run to three lines
   and swallow the frame, so this tops out lower and clamps the overflow. */
.nst-hero__title { margin: 0; font-family: var(--cond); font-size: clamp(38px, 4.4vw, 64px); line-height: .94; font-weight: 700; letter-spacing: -.01em; text-transform: uppercase; text-wrap: balance; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.nst-hero__caption { margin: 0; font-size: 19px; line-height: 1.5; color: var(--muted); max-width: 560px; }
.nst-hero__actions { display: flex; gap: 12px; padding-top: 6px; }

/* No rule down the left: the gap between the player and the frames separates
   them, and a line there boxed the rail in. */
.nst-upnext { display: flex; flex-direction: column; min-height: 0; height: 100%; overflow: hidden; }
/* The queue scrolls inside the hero's height so the rail never drives the page
   taller than the moment beside it. */
.nst-upnext [data-upnext] { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--edge) transparent; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px 10px; padding: 4px 12px 14px; align-content: start; }
.nst-upnext [data-upnext]::-webkit-scrollbar { width: 6px; }
.nst-upnext [data-upnext]::-webkit-scrollbar-thumb { background: var(--edge); border-radius: 999px; }
.nst-upnext [data-upnext]::-webkit-scrollbar-track { background: transparent; }
.nst-upnext__head { flex-shrink: 0; padding: 16px 20px 12px; display: flex; align-items: baseline; justify-content: space-between; font-family: var(--mono); font-size: 11px; letter-spacing: .2em; color: var(--orange); }
.nst-upnext__head > span:last-child { color: var(--dim); letter-spacing: 0; }
/* No rule between rows: the thumbnails already give the list its rhythm, and a
   divider on every row made a short list look like a table. */
/* Frames, not a list of links, and vertical because the film is -- nearly all
   of it is shot 9:16 on a phone.

   Two columns, because one cannot hold a portrait frame: at the rail's width a
   9:16 item is 517px tall and less than one and a half fit above the fold,
   which is a poster rather than a rail. Two-up puts the frame at 137px, and
   the caption goes under it rather than over: laid on the picture at that
   width it covered the thing it was captioning. */
.nst-upnext__item { position: relative; display: block; padding: 0; margin: 0; background: none; transition: transform .22s ease; }
.nst-upnext__item:hover { transform: translateY(-2px); }
/* Which row is on the big screen. Without it the queue gives no sense of place
   once you have clicked through a few. */
/* On a frame, an outline says "this one" better than a tint -- a wash over
   footage just looks like the picture is orange. */
.nst-upnext__item.is-playing .nst-upnext__thumb { outline: 2px solid var(--orange); outline-offset: -2px; }
.nst-upnext__item.is-playing .nst-upnext__title { color: var(--orange); }
.nst-upnext__text { position: static; min-width: 0; padding: 6px 1px 0; }
.nst-upnext__who { display: flex; align-items: center; gap: 8px; }
.nst-upnext__name { font-family: var(--mono); font-size: 9px; letter-spacing: .1em; color: var(--muted); text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-top: 4px; }
/* The count on its own line, in the accent, so it reads as the figure it is
   rather than the tail of the athlete's name. */
.nst-upnext__plays { font-family: var(--mono); font-size: 9px; letter-spacing: .1em; color: var(--orange); text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-top: 2px; }
/* Round and quiet in the list: a square block at this size reads as a button
   and competes with the thumbnail beside it. */
.nst-upnext__time { position: absolute; right: 6px; top: 6px; bottom: auto; padding: 2px 5px; border-radius: var(--r-sm); background: rgba(8,8,10,.86); font-family: var(--mono); font-size: 10px; font-weight: 500; letter-spacing: .03em; color: var(--text); }
.nst-upnext__time[hidden] { display: none; }
/* 4:5 rather than the 9:16 the film is shot in: two rows of four have to fit
   the rail above the fold, and a true portrait frame makes each item 287px --
   585px for two rows in a 510px space. This is the tallest that still fits
   four with the name and the count under each. */
.nst-upnext__thumb { position: relative; width: 100%; aspect-ratio: 4 / 5; background: var(--hatch); overflow: hidden; border-radius: var(--r-sm); }
.nst-upnext__thumb img { transition: transform .5s cubic-bezier(.22,.61,.36,1); }
.nst-upnext__item:hover .nst-upnext__thumb img { transform: scale(1.04); }
/* The copy sits on the footage, so it needs its own ground to stay legible
   against a bright frame. */
.nst-upnext__thumb::after { content: ""; position: absolute; inset: 0; background: linear-gradient(#0000 62%, rgba(8,8,10,.55)); }
.nst-upnext__kicker { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; color: var(--dim); text-transform: uppercase; padding-top: 3px; }
/* Two lines maximum: a three-line headline pushed each row past 170px and only
   four fitted in the rail. */
/* Two lines are reserved whether or not the title needs both. It clamps at two
   either way, so a one-line title used to pull its athlete, plays and section
   up a line while the card beside it kept them low -- every row of the rail
   sat at a different height and the column read as ragged. min-height holds
   the space so everything below the title lines up across the grid. */
.nst-upnext__title { font-family: var(--cond); font-size: 14px; line-height: 1.08; text-transform: uppercase; font-weight: 600; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: calc(2 * 1.08em); }
/* The label reads as the last line of the hero, so it sits on the hero's own
   bottom edge rather than floating above it. */
.nst-upnext__foot { flex-shrink: 0; margin-top: 0; padding: 18px 22px 16px; border-top: 1px solid var(--line); font-family: var(--mono); font-size: 11px; letter-spacing: .16em; color: var(--dim); }

/* --- still playing -------------------------------------------------------- */

/* The transport reads as glass over the clip rather than a solid shelf under
   it. It was painting flat --panel, which cut a hard grey band across the
   bottom of the film -- the @supports rule below already assumed a blur here,
   so the fallback existed before the thing it was falling back from. */
.nst-playing { display: grid; grid-template-columns: 96px auto minmax(200px, 1fr) minmax(160px, 2fr) auto auto; align-items: center; gap: 16px; padding: 10px var(--gutter); background: color-mix(in srgb, var(--panel) 62%, transparent); backdrop-filter: blur(22px) saturate(140%); -webkit-backdrop-filter: blur(22px) saturate(140%); }
.nst-playing[hidden] { display: none; }
.nst-playing__thumb { width: 96px; height: 54px; background: var(--hatch); border-radius: var(--r-sm); overflow: hidden; }
.nst-playing__thumb video, .nst-playing__thumb img { width: 100%; height: 100%; object-fit: cover; }
.nst-playing__label { font-family: var(--mono); font-size: 10px; letter-spacing: .2em; color: var(--orange); }
/* Two rows: what is playing, and what follows it. */
.nst-playing__text { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.nst-playing__next { display: flex; align-items: center; gap: 10px; min-width: 0; }
.nst-playing__next[hidden] { display: none; }
.nst-playing__nextlabel { font-family: var(--mono); font-size: 9px; letter-spacing: .18em; color: var(--dim); flex-shrink: 0; }
.nst-playing__nexttitle { font-family: var(--mono); font-size: 10px; letter-spacing: .06em; color: var(--muted); text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nst-playing__countdown { font-family: var(--mono); font-size: 10px; font-weight: 700; color: var(--orange); flex-shrink: 0; }
.nst-playing__next.is-counting .nst-playing__nexttitle { color: var(--text); }
.nst-playing__skip, .nst-playing__cancel { flex-shrink: 0; background: none; border: 1px solid var(--edge-strong); border-radius: var(--r-sm); color: var(--muted); font-family: var(--mono); font-size: 8px; letter-spacing: .12em; padding: 3px 7px; cursor: pointer; }
.nst-playing__skip:hover { border-color: var(--orange); color: var(--orange); }
.nst-playing__cancel:hover { border-color: var(--text); color: var(--text); }
.nst-playing__skip[hidden], .nst-playing__cancel[hidden] { display: none; }

.nst-playing__title { font-family: var(--cond); font-size: 20px; font-weight: 700; text-transform: uppercase; line-height: 1.05; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Tall enough to grab: a 3px line is a readout, not a scrubber. */
.nst-playing__track { position: relative; height: 4px; border-radius: 999px; background: rgba(244,244,242,.16); cursor: pointer; touch-action: none; }
.nst-playing__track::before { content: ""; position: absolute; inset: -10px 0; }
.nst-playing__track:hover { background: rgba(244,244,242,.26); }
.nst-playing__track:focus-visible { outline: 2px solid var(--orange); outline-offset: 6px; }
.nst-playing__btn { width: 34px; height: 34px; flex-shrink: 0; border: 0; border-radius: 50%; background: rgba(244,244,242,.12); color: var(--text); font-size: 13px; line-height: 1; cursor: pointer; transition: background .2s ease, color .2s ease; display: grid; place-items: center; }
.nst-playing__btn svg { width: 15px; height: 15px; display: block; }
/* Quieter than the toggle either side of it: play and pause is the control,
   and stepping the rail is the thing you reach for after it. */
.nst-playing__btn--step { width: 30px; height: 30px; background: none; color: var(--muted, var(--muted)); }
.nst-playing__btn--step svg { width: 13px; height: 13px; }
.nst-playing__btn--step[hidden] { display: none; }
/* The three of them in the one column the toggle used to hold. The bar is a
   fixed six-column grid, so adding two children pushed everything along by two
   and the sound button wrapped onto a second row. */
.nst-playing__transport { display: flex; align-items: center; gap: 8px; }
/* Only where a pointer can actually hover. A tap on a phone leaves the button
   in :hover until something else is touched, and an unmuted button is orange on
   orange in that state -- a solid disc with the icon invisible inside it. */
@media (hover: hover) {
  .nst-playing__btn:hover { background: var(--orange); color: var(--ink); }
}
.nst-playing__btn.is-live { color: var(--orange); }
/* The live colour has to win over the hover fill, or unmuting with a pointer
   still paints orange on orange. */
.nst-playing__btn.is-live:hover { background: rgba(255,74,21,.16); color: var(--orange); }
.nst-playing__track > span { display: block; width: 0; height: 100%; border-radius: 999px; background: var(--orange); transition: width .3s linear; }
.nst-playing__time { font-family: var(--mono); font-size: 11px; color: var(--dim); white-space: nowrap; }

/* --- stats ---------------------------------------------------------------- */

/* A strip, not four boxes. The figures sat in tall panels with a lot of air
   above and below, which read as a gap in the page rather than a summary of it
   -- label and number now share a baseline and the row is half the height. */
.nst-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--line); border-bottom: 1px solid var(--line); }
.nst-stat { display: flex; align-items: baseline; gap: 12px; padding: 18px var(--gutter); background: var(--ink); border-right: 0; }
.nst-stat:last-child { border-right: 0; }
/* line-height: 1 made the line box exactly one em, which is shorter than the
   glyphs themselves -- round figures overshoot the baseline and cap height, so
   the bottoms of 0, 3 and 6 were shaved flat. The extra tenth gives them room
   without moving the baseline the label aligns to. */
.nst-stat__value { font-family: var(--cond); font-size: 42px; font-weight: 700; line-height: 1.1; letter-spacing: -.01em; flex-shrink: 0; }
.nst-stat--accent .nst-stat__value { color: var(--orange); }
.nst-stat__label { font-family: var(--mono); font-size: 10px; letter-spacing: .16em; color: var(--dim); text-transform: uppercase; line-height: 1.35; }

/* --- sections ------------------------------------------------------------- */

.nst-section { padding: 72px var(--gutter) 0; }
/* No rule above it: the change of ground colour already separates this block
   from the section before it, and the line on top of that read as a seam. */
/* Its own panel, so this bottom padding sits inside the tint rather than
   between sections -- and By sport then adds its own 72 on top of it. At 76
   that came to 148px above By sport against 72 below, and the section sat low
   between the two instead of centred. Enough here to close the panel under
   the button; the rest of the gap is the page's own. */
.nst-section--roster { margin-top: 72px; padding: 56px var(--gutter) 12px; background: var(--panel); }
/* No rule under the heading: the tiles now carry their own corners and a real
   gutter, so the section already reads as separate from the one above it. The
   hairline was a second, weaker way of saying the same thing. */
.nst-section__head { display: flex; align-items: baseline; gap: 20px; padding-bottom: 14px; }
.nst-section__head--big { align-items: flex-end; padding-bottom: 26px; }
.nst-section__head h2 { margin: 0; font-family: var(--cond); font-size: 52px; font-weight: 700; text-transform: uppercase; line-height: 1; }
.nst-section__meta { font-family: var(--mono); font-size: 11px; letter-spacing: .18em; color: var(--dim); text-transform: uppercase; }
.nst-section__all { margin-left: auto; font-family: var(--mono); font-size: 11px; letter-spacing: .18em; color: var(--orange); cursor: pointer; }
/* Wears the accent the rest of the row does, but reports a number rather than
   leading anywhere -- the way into the directory is the button under the grid. */
.nst-section__all--static { cursor: default; }
/* Way into the directory, under the roster grid. Placement only -- the look
   comes from .nst-btn--orange, the same button the header uses for its primary
   action.

   Not ".nst-more": the directory view already owns that name for its
   infinite-scroll sentinel, and this rule was reaching it. */
.nst-seeall { display: flex; margin: 24px auto 0; justify-content: center; }
.nst-eyebrow { font-family: var(--mono); font-size: 11px; letter-spacing: .2em; color: var(--orange); }
.nst-display { margin: 0; font-family: var(--cond); font-size: 68px; font-weight: 700; text-transform: uppercase; line-height: 1; padding-top: 8px; }

/* Offers. The biggest of the week sits beside a programme ranking, and the run
   of recent offers reads as a table -- date, school, athlete, clip. The old
   layout put three headlines on flat black, which told a reader nothing about
   who was offering or to whom. */
.nst-offers { display: grid; grid-template-columns: 1fr 320px; gap: 14px; padding-top: 14px; }

/* An offer post's image is the school crest, so it reads as a mark set beside
   the copy. Full-bleed it swamped the card and the athlete's name sat
   unreadable on top of a logo. */
/* The border was a flat orange rectangle around a dark card, which reads as a
   warning rather than a feature. The card is lit from the corner the crest sits
   in and keeps only a hairline of the accent, so the eye goes to the athlete's
   name rather than to the outline. */
/* Flat. The card was a lifted pane -- rounded, outlined and dropped on a
   shadow -- which reads as a tile on a page rather than the top of a section.
   It keeps the rule down its accent edge and nothing else: no shadow, no
   border round the other three sides, no corner. */
/* The week's biggest moment, at the size that claims. The right column was
   180px, which left the copy stopping around 575px and roughly 460px of empty
   page between the two -- and made the section's lead read smaller than the
   three columns of rows beneath it. The film takes the right half now. */
.nst-biggest { position: relative; min-height: 300px; display: grid; grid-template-columns: minmax(0, 1fr) minmax(420px, 46%); gap: 40px; align-items: center; overflow: hidden; background: none; border: 0; border-radius: 0; padding: 26px 0 30px 26px; }
/* No wash. The card and the rail beside it were both laid over an orange
   gradient, so the whole section read as accent before anything on it had said
   a word -- and every mark that is meant to stand out was standing out against
   more of itself. */
/* A rule down the accent edge, so the card is anchored on one side rather than
   boxed on four. */
/* A mark at the top of the edge rather than a bar down the whole of it. Full
   height it is the tallest piece of orange on the page, competing with the
   figures and the tags for the same attention. */
/* No rule down the edge. The card is the first thing in the section and the
   tag on it already says what it is -- a line beside the picture was one more
   accent competing with the tag, the date, the crest and the figures. */
/* Nothing to show, no column held open for it: an empty crest left a fifth of
   the card as blank card colour beside the copy. */
.nst-biggest:not(:has(.nst-biggest__crest img)) { grid-template-columns: 1fr; }
/* An empty frame is hidden; a filled one is not. This asked for an img, and
   the lead mounts a video where the moment has film -- so the whole frame
   collapsed and the grid fell back to one column. */
.nst-biggest:not(:has(.nst-biggest__crest img)) .nst-biggest__crest { display: none; }
.nst-biggest__crest { position: relative; z-index: 2; grid-column: 2; width: 100%; aspect-ratio: 16 / 10; display: grid; place-items: center; justify-self: stretch; border-radius: var(--r); overflow: hidden; background: var(--well); }
/* A still fills the frame the same way the film does -- contain left a crest
   floating in the middle of a wide box with the surface showing around it. */
.nst-biggest__crest img { width: 100%; height: 100%; object-fit: cover; border-radius: 0; }
/* What the moment earned, under the words. */
.nst-biggest__foot { display: flex; align-items: baseline; gap: 14px; padding-top: 4px; font-family: var(--mono); font-size: 11px; letter-spacing: .14em; }
.nst-biggest__reacts { color: var(--orange); font-weight: 600; }
.nst-biggest__when { color: var(--label); }
/* Between the desktop layout and the phone sheet's own. A 420px frame beside
   copy stops working well before 760px, so in that band the film goes full
   width above the words. Below 760 the mobile sheet takes over with its own
   side-by-side arrangement, so this stops there rather than fighting it. */
@media (max-width: 900px) and (min-width: 761px) {
  .nst-biggest { grid-template-columns: minmax(0, 1fr); gap: 22px; padding-right: 26px; }
  .nst-biggest__crest { grid-column: 1; grid-row: 1; aspect-ratio: 16 / 9; }
  .nst-biggest__body { grid-column: 1; grid-row: 2; }
}
.nst-biggest__body { grid-column: 1; grid-row: 1; position: relative; z-index: 2; padding: 0; display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* The date next to the tag comes down to grey. A solid orange block and an
   orange line beside it are two accents on one row, and only one of them is
   the point. */
/* The date next to the tag comes down to grey. A solid orange block and an
   orange line beside it are two accents on one row, and only one is the point. */
.nst-biggest__flag { display: flex; align-items: center; gap: 12px; }
.nst-biggest__flag .nst-kicker { color: var(--dim); }
.nst-biggest__flag .nst-kicker { color: var(--dim); }
.nst-tag { background: var(--orange); color: var(--ink); font-family: var(--mono); font-size: 9px; font-weight: 700; letter-spacing: .14em; padding: 4px 8px; border-radius: var(--r-sm); }
/* Athlete first, school second: whose offer it is matters more than which
   programme sent it, and the old order buried the name in small grey type. */
.nst-biggest__who { display: flex; align-items: center; gap: 14px; padding-top: 4px; }
.nst-biggest__name { font-family: var(--cond); font-size: clamp(30px, 3.2vw, 46px); font-weight: 700; text-transform: uppercase; line-height: .98; }
.nst-biggest__line { font-family: var(--mono); font-size: 10px; letter-spacing: .16em; color: var(--dim); padding-top: 5px; }
/* The programme, at the size the news deserves. A bare COMMITTED pill beside
   BIGGEST OF THE WEEK was two chips doing one job and carrying nothing; what
   gives this card its weight is the school's own name. The stage rides small
   beside it rather than standing alone. */
.nst-biggest__from { display: flex; align-items: center; gap: 11px; flex-wrap: wrap; padding-top: 2px; }
.nst-biggest__mark { width: 22px; height: 22px; flex-shrink: 0; color: var(--orange); display: block; }
.nst-biggest__mark svg { width: 100%; height: 100%; display: block; }
.nst-biggest__prog { font-family: var(--cond); font-size: clamp(24px, 2.4vw, 34px); font-weight: 700; text-transform: uppercase; line-height: 1; }
.nst-biggest__stage { font-family: var(--mono); font-size: 10px; letter-spacing: .18em; color: var(--dim); padding-top: 2px; }
.nst-biggest__title { margin: 0; font-family: var(--cond); font-size: clamp(26px, 2.6vw, 38px); font-weight: 700; text-transform: uppercase; line-height: 1; max-width: 18ch; }
.nst-biggest__caption { margin: 0; font-size: 15px; line-height: 1.5; color: var(--muted); max-width: 44ch; margin-top: auto; }

/* A pane, not a grey box parked beside the lead. */
/* Flat too, and separated from the lead by a rule rather than by being a box
   beside a box. */
.nst-programs { position: relative; background: none; border: 0; border-left: 1px solid var(--line); border-radius: 0; padding: 4px 0 20px 26px; display: flex; flex-direction: column; overflow: hidden; }

.nst-programs > * { position: relative; z-index: 1; }
.nst-programs__head { font-family: var(--mono); font-size: 10px; letter-spacing: .18em; color: var(--orange); padding-bottom: 14px; }
/* The rows separate on a hairline instead of floating, and the leader is the
   only one set in full white -- a ranking should say who is first without the
   reader counting. */
.nst-program { display: grid; grid-template-columns: 16px 1fr 56px 22px; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--line-glass); }
.nst-program:last-of-type { border-bottom: 0; }
.nst-program:first-of-type .nst-program__name { color: var(--text); }
.nst-program:first-of-type .nst-program__rank { color: var(--orange); }
.nst-program__rank { font-family: var(--mono); font-size: 10px; color: var(--faint); }
.nst-program__name { font-family: var(--cond); font-size: 17px; font-weight: 700; text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nst-program__count { font-family: var(--mono); font-size: 11px; color: var(--muted); text-align: right; }
.nst-programs__foot { margin-top: auto; padding-top: 18px; border-top: 1px solid var(--line); display: flex; gap: 32px; }
.nst-programs__foot > div { display: flex; flex-direction: column; gap: 4px; }
.nst-programs__foot span:last-child { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; color: var(--dim); }
.nst-programs__num { font-family: var(--cond); font-size: 34px; font-weight: 700; line-height: 1; }
.nst-programs__num--accent { color: var(--orange); }

.nst-offertable { padding-top: 14px; }
/* Tighter, and the rule between rows is lighter than the one under the head --
   a ledger reads as a set of entries under a heading rather than a stack of
   equally boxed cells. The row lifts its own thumbnail on hover instead of
   washing the whole line grey. */
.nst-offerrow { display: grid; grid-template-columns: 62px minmax(0, 1.2fr) minmax(0, 1fr) 90px 92px; align-items: center; gap: 18px; padding: 11px 6px; border-bottom: 1px solid var(--line-glass); transition: background .18s ease; }
.nst-offerrow--head { padding: 10px 6px; border-bottom: 1px solid var(--line-strong); font-family: var(--mono); font-size: 9px; letter-spacing: .16em; color: var(--dim); }
.nst-offerrow--head:hover { background: none; }
.nst-offerrow:last-child { border-bottom: 0; }
.nst-offerrow:not(.nst-offerrow--head):hover { background: rgba(244,244,242,.025); }
.nst-offerrow:not(.nst-offerrow--head):hover .nst-offerrow__thumb img { transform: scale(1.06); }
/* The line the reader is on, marked on the edge rather than under everything. */
.nst-offerrow:not(.nst-offerrow--head)::before { content: ""; position: absolute; left: 0; width: 2px; height: 100%; background: var(--orange); opacity: 0; transition: opacity .18s ease; }
.nst-offerrow:not(.nst-offerrow--head) { position: relative; }
.nst-offerrow:not(.nst-offerrow--head):hover::before { opacity: 1; }
.nst-offerrow__date { font-family: var(--mono); font-size: 10px; color: var(--dim); }
.nst-offerrow__school--none { color: var(--faint); }
.nst-offerrow__school { font-family: var(--cond); font-size: 18px; font-weight: 700; text-transform: uppercase; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nst-offerrow__who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.nst-offerrow__whotext { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.nst-offerrow__name { font-family: var(--cond); font-size: 19px; font-weight: 700; text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nst-offerrow__meta { font-family: var(--mono); font-size: 9px; letter-spacing: .12em; color: var(--dim); }
.nst-offerrow__tag:not(:empty) { font-family: var(--mono); font-size: 9px; letter-spacing: .12em; color: var(--orange); border: 1px solid rgba(255,74,21,.4); border-radius: var(--r-sm); padding: 4px 7px; justify-self: start; }
/* 16:9 and wider. At 74x44 the frames were too small to read as pictures and
   the column looked like a row of smudges. */
.nst-offerrow__thumb { width: 92px; aspect-ratio: 16 / 10; height: auto; border-radius: var(--r-sm); overflow: hidden; background: var(--hatch); }
.nst-offerrow__thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .4s cubic-bezier(.22,.61,.36,1); }
.nst-offerrow__all { display: block; margin: 22px auto 0; padding: 12px 26px; background: none; border: 1px solid var(--edge-strong); border-radius: var(--r-sm); color: var(--text); font-family: var(--cond); font-size: 13px; letter-spacing: .16em; cursor: pointer; }
.nst-offerrow__all:hover { border-color: var(--orange); color: var(--orange); }

.nst-kicker { font-family: var(--mono); font-size: 10px; letter-spacing: .2em; color: var(--orange); text-transform: uppercase; }

/* Uniform card grids: every tile the same height, so mixed portrait and
   landscape media cannot leave the ragged edges the old rails had. */
.nst-grid { display: grid; gap: 14px; padding-top: 14px; }
.nst-grid--3 { grid-template-columns: repeat(3, 1fr); }
.nst-grid--4 { grid-template-columns: repeat(4, 1fr); }
.nst-card { position: relative; height: 400px; background: var(--hatch); overflow: hidden;  border-radius: var(--r); }
/* Roster cards open the athlete, so they have to read as something you can
   press rather than as a picture of one. */
.nst-card--live { cursor: pointer; transition: transform .18s ease, box-shadow .18s ease; }
.nst-card--live:hover { transform: translateY(-2px); box-shadow: 0 12px 32px #00000066; }
.nst-card--live:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nst-grid--4 .nst-card { height: 440px; }
.nst-card img { position: absolute; inset: 0; }
.nst-card__scrim { position: absolute; inset: 0; background: linear-gradient(#0000 45%, #08080af0); }
.nst-card__body { position: absolute; left: 22px; bottom: 22px; right: 22px; }
.nst-card__title { font-family: var(--cond); font-size: 30px; font-weight: 600; text-transform: uppercase; line-height: 1.04; padding-top: 8px; }
.nst-card__who { display: flex; align-items: center; gap: 8px; padding-top: 10px; }
.nst-card__author { font-family: var(--mono); font-size: 9px; letter-spacing: .14em; color: var(--muted); text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The roster card already names the athlete in its headline. */
.nst-section--roster .nst-card__who { display: none; }
.nst-card__index { position: absolute; top: 16px; right: 18px; font-family: var(--mono); font-size: 11px; color: var(--orange); }
.nst-card__name { font-family: var(--cond); font-size: 36px; font-weight: 700; text-transform: uppercase; line-height: .98; }
/* Roster cards. The name sat mid-card over whatever the poster happened to
   show -- a scoreboard, a caption already burned into the image -- so it is
   anchored to the bottom on its own dark band instead. */
.nst-section--roster .nst-card { display: flex; flex-direction: column; justify-content: flex-end; }
/* The picture, its wash and the rank sit in a frame of their own so mobile can
   lift the name out from over the face. Here the frame simply fills the card,
   which is what the image did before it existed. */
.nst-section--roster .nst-card__frame { position: absolute; inset: 0; }
.nst-section--roster .nst-card__scrim { background: linear-gradient(#0000 38%, rgba(8,8,10,.72) 62%, rgba(8,8,10,.97)); }
.nst-section--roster .nst-card__body { position: relative; inset: auto; padding: 0 18px 18px; }
.nst-card__identity { display: flex; align-items: center; gap: 10px; padding-bottom: 8px; }
.nst-section--roster .nst-card__name { font-size: 28px; line-height: .96; }
.nst-section--roster .nst-avatar { width: 34px; height: 34px; font-size: 15px; border-radius: 50%; border-color: rgba(244,244,242,.28); }
.nst-section--roster .nst-card__line { padding-top: 2px; }
.nst-section--roster .nst-card__index { background: rgba(8,8,10,.66); border-radius: 50%; width: 24px; height: 24px; display: grid; place-items: center; font-size: 9px; }
/* Square to match the comp's hard edges. Falls back to the initial, since only
   a handful of apps have set a profile image. */
.nst-hero__by { display: inline-flex; align-items: center; gap: 8px; }
/* The name opens the athlete, so it carries a link's affordance. Styled off the
   player's byline button so the two ways into an athlete read the same. */
.nst-hero__who {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit;
  color: inherit;
}
.nst-hero__who:hover, .nst-hero__who:focus-visible { text-decoration: underline; }
.nst-hero__who:disabled { cursor: default; text-decoration: none; }
/* Not part of the link: the sport describes the moment, not a place to go. */
.nst-hero__sport:empty { display: none; }
.nst-avatar { position: relative; width: 44px; height: 44px; flex-shrink: 0; background: #1b1b22; border: 1px solid var(--edge-strong); overflow: hidden; display: grid; place-items: center; font-family: var(--cond); font-size: 22px; font-weight: 700; color: var(--muted);  border-radius: var(--r-sm); }
.nst-avatar--md { width: 54px; height: 54px; font-size: 24px; border-radius: 50%; }
.nst-avatar--sm { width: 26px; height: 26px; font-size: 13px; border-radius: 50%; }
/* Every size modifier lives here, directly after the base. Scattered further up
   the file they lost to it on source order and silently rendered at 44px. */
.nst-avatar--xs { width: 18px; height: 18px; font-size: 9px; border-radius: 50%; background: #16161c; border-color: var(--line-strong); color: var(--dim); }
.nst-avatar__initial { position: absolute; inset: 0; display: grid; place-items: center; }
.nst-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .3s ease; }
.nst-avatar img.is-loaded { opacity: 1; }
.nst-card__line { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; color: var(--label); padding-top: 10px; text-transform: uppercase; }
.nst-card__line--accent { color: var(--orange); padding-top: 4px; }

.nst-chips { display: flex; gap: 8px; font-family: var(--cond); font-size: 14px; letter-spacing: .14em; flex-wrap: wrap; }
.nst-chip { padding: 11px 18px; border: 1px solid var(--edge-strong); color: var(--muted);  border-radius: var(--r-sm); }
.nst-chip--on { background: var(--text); color: var(--ink); font-weight: 600; border-color: var(--text); }

/* Tables: by-sport and the climb both read as ranked rows, not tiles. */
.nst-table { display: grid; gap: 18px; align-items: center; padding: 18px 0; border-bottom: 1px solid var(--line); }
.nst-table--sport { grid-template-columns: 56px 1fr 300px 130px 130px; gap: 20px; padding: 20px 0; }
.nst-table__head { padding: 14px 0; font-family: var(--mono); font-size: 10px; letter-spacing: .16em; color: var(--dim); }
.nst-right { text-align: right; }
.nst-sport__name { font-family: var(--cond); font-size: 40px; font-weight: 700; text-transform: uppercase; line-height: 1; }
.nst-sport__name--muted { color: var(--label); }
.nst-bar { display: block; height: 8px; background: var(--line);  border-radius: 999px; }
.nst-bar > span { display: block; height: 8px; background: var(--orange);  border-radius: 999px; }
.nst-num { font-family: var(--cond); font-size: 34px; font-weight: 700; text-align: right; }
.nst-num--muted { color: var(--muted); }
.nst-icon { width: 40px; height: 40px; }

/* Athletes to watch. A reader scanning this wants faces and film, so it is a
   grid of their own frames rather than a table of counts. */
/* A path through the section rather than a wall of boxes: an athlete on the
   left, the next on the right, and a line drawn between them the whole way
   down. It was a three-across grid of 420px posters, which on a phone fell to
   one column -- nine full-height blocks stacked, most of them showing the
   empty hatch because the posters they drew were dead links. */
.nst-watchgrid {
  display: flex; flex-direction: column; gap: 0; padding-top: 14px;
  /* Read down rather than across, so it keeps a column worth reading instead
     of stranding each name against 1300px of empty row. */
  max-width: 620px; margin-inline: auto;
}
/* Three columns, and the node takes the left or the middle one. That keeps the
   two sides a fixed 38% apart, which with the row height below leans the join
   near 45 degrees. Two columns could not do it: whichever share the text was
   given, the node stayed pinned at the row's edge and the sides ended up 334px
   apart with 115px between them vertically -- 72 degrees off vertical, so the
   line ran nearly sideways and left the screen before reaching the node below. */
/* Every row the same height, whatever it carries. Left to their contents the
   rows came out 112px for a bare name and up to 148px for one with a position
   and a class year on it -- so the diagonal between two nodes changed angle
   at every step and the run read as a stagger rather than a path.

   The height is set below 901px only: above that the path turns ninety degrees
   and runs across the section, where the rows are columns and a floor on their
   height means nothing. */
.nst-watchcard {
  position: relative; display: grid;
  grid-template-columns: 64px 38% 1fr;
  align-items: center; gap: 16px; padding: 0; cursor: pointer;
  background: none; border-radius: 0; height: auto; overflow: visible;
}
.nst-watchcard .nst-watchcard__thread { grid-column: 1; }
.nst-watchcard .nst-watchcard__body { grid-column: 2 / span 2; }
/* Every other athlete swaps sides: the node moves to the middle column and the
   text takes the two beside it, so the name stays on the row's open side. */
.nst-watchcard:nth-child(even) .nst-watchcard__thread { grid-column: 2; justify-self: center; }
.nst-watchcard:nth-child(even) .nst-watchcard__body { grid-column: 3; }

.nst-watchcard__thread {
  position: relative; align-self: stretch; height: 100%;
  display: flex; align-items: center; justify-content: center;
}
/* Length and lean are measured and written by the script -- they depend on
   where the two nodes actually land, and the drop between them is whatever
   that row's name and tags come to. These are the values for the frame before
   it runs. */
/* Centred by a margin rather than a translate. Written as
   translateX(-50%) rotate(...) the shift is applied along the already-rotated
   axis, which walked the whole run sideways -- a line meant for the athlete
   below and to the right was drawn heading down and to the left, ending in
   empty space. The margin does the centring before any rotation happens. */
.nst-watchcard__track {
  position: absolute; top: 50%; left: 50%; width: 2px; margin-left: -1px;
  height: var(--nst-run, 0px);
  background: #33333c; transform-origin: top center;
  transform: rotate(var(--nst-lean, 0deg));
}
.nst-watchcard__node {
  position: relative; z-index: 1; width: 52px; height: 52px; border-radius: 50%;
  /* Sat on the path rather than crossed by it -- the ring is the page's own
     background, so the line appears to pass behind the picture. */
  box-shadow: 0 0 0 4px var(--bg, var(--ink));
  transition: border-color .2s ease, transform .2s ease;
}
.nst-watchcard:hover .nst-watchcard__node { border-color: var(--orange); transform: scale(1.06); }
/* A clip behind them earns the orange ring; the rest stay quiet. */
.nst-watchcard.has-video .nst-watchcard__node { border-color: #ff4a1566; }
.nst-watchcard__body { position: static; z-index: 1; min-width: 0; }
/* No poster behind it any more, so the name drops the shadow it needed to sit
   on burned-in type and comes down to a size that belongs beside a node. */
.nst-watchcard__name { font-family: var(--cond); font-size: 20px; font-weight: 700; text-transform: uppercase; line-height: 1.1; text-shadow: none; overflow-wrap: anywhere; }
.nst-watchcard:hover .nst-watchcard__name { color: var(--orange); }
/* One line, clipped rather than wrapped. "FOOTBALL · QUARTERBACK" broke to a
   second line and pushed that athlete's tags out of step with the rest. */
.nst-watchcard__line { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; color: var(--label); padding-top: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Outlined, not the solid orange badge the hero uses -- three of these sit on
   one card and a row of solid blocks would outshout the name above them. */
/* On one line, and never wrapping to a second: a wrapped pair is what pushed a
   row 36px past its neighbours. */
.nst-watchcard__tags { display: flex; gap: 6px; flex-wrap: nowrap; padding-top: 8px; }
.nst-watchcard__tags .nst-tag { background: none; color: var(--muted); border: 1px solid var(--edge-strong); font-weight: 400; font-size: 9px; letter-spacing: .1em; padding: 4px 8px; border-radius: var(--r-sm); }
.nst-watchcard__tags .nst-tag--offers { color: var(--orange); border-color: #ff4a1555; }

/* The drop between two nodes, and so the angle of the line joining them. Deep
   enough that the lean stays near 45: at the rows' own height the nodes sat
   153px apart across and 40px down, which is 75 degrees -- a rule under the
   row rather than a path down the section. */
@media (max-width: 900px) {
  /* The same run as the desktop, at phone size: across rather than down, each
     athlete alternating high and low with the line leaning between them.

     Down the page it cost a screen and a half for five athletes and read as a
     list with a rule beside it. Across, the whole run is one band -- and it
     scrolls sideways, so however many there are it stays one band.

     The script measures where the nodes actually land and writes each line's
     length and lean from that, so it follows this without knowing about it. */
  .nst-watchgrid {
    display: flex; flex-direction: row; align-items: stretch;
    gap: 0; max-width: none; margin-inline: 0;
    overflow-x: auto; scroll-snap-type: x proximity;
    scroll-padding-left: var(--gutter);
    padding: 8px var(--gutter) 12px;
    scrollbar-width: none;
  }
  .nst-watchgrid::-webkit-scrollbar { display: none; }

  /* A fixed width here rather than a share of the row: there is not enough
     phone to divide five ways and still show a face, so they run past the
     edge and the reader scrolls to them. */
  .nst-watchcard {
    min-height: 0;
    grid-template-columns: 1fr;
    /* The node row is taller than the node, and the two sides sit at opposite
       ends of it -- that gap is the drop, and the drop is what gives the line
       its lean. At 76px the two sat 14px apart and the join came out nearly
       flat, which reads as a rule under the band rather than a path along it. */
    grid-template-rows: 118px auto;
    flex: 0 0 124px; width: 124px;
    /* No row gap: it added to the drop the words already have to make up, and
       the name belongs directly under its own face. */
    gap: 0; padding: 22px 0;
    justify-items: center; text-align: center;
    scroll-snap-align: start;
  }
  /* Node above, words under -- one column, like the desktop run. */
  .nst-watchcard .nst-watchcard__thread,
  .nst-watchcard:nth-child(even) .nst-watchcard__thread {
    grid-column: 1; grid-row: 1; justify-self: center;
  }
  /* Each name drops by whatever its own node did: the row is 118px against a
     62px node, so a low node has taken 56px and the words follow it down.
     Level, they sat on one baseline while the nodes alternated above them --
     66px clear of one face and 10px from the next. */
  .nst-watchcard .nst-watchcard__body,
  .nst-watchcard:nth-child(even) .nst-watchcard__body {
    grid-column: 1; grid-row: 2; align-self: start;
  }
  .nst-watchcard:nth-child(odd) .nst-watchcard__body { padding-top: 0; }
  .nst-watchcard:nth-child(even) .nst-watchcard__body { padding-top: 56px; }
  /* The drop between the two sides, which is what gives the join its lean.
     Level nodes would draw a flat rule through the band. */
  .nst-watchcard:nth-child(odd) .nst-watchcard__thread { align-items: flex-start; }
  .nst-watchcard:nth-child(even) .nst-watchcard__thread { align-items: flex-end; }

  .nst-watchcard__track { display: block; }
  .nst-watchcard__node { width: 62px; height: 62px; }
  .nst-watchcard__name { font-size: 12px; line-height: 1.15; }
  .nst-watchcard__line { padding-top: 3px; font-size: 8px; }
  .nst-watchcard__tags { justify-content: center; padding-top: 5px; }
  .nst-watchcard__tags .nst-tag { font-size: 8px; padding: 3px 6px; }
}

/* Across, not down, once there is room for it. The path was built for a phone
   -- a single 620px column, each athlete under the last -- and kept that shape
   at every width, so on a desktop it ran the height of the page with thirteen
   hundred pixels of black beside it and only four athletes visible at a time.
   The run reads left to right here, alternating high and low, which is the
   same zigzag turned on its side: seven athletes in one screen.

   The script measures where the nodes actually land and writes the length and
   the lean from that, so it follows this without knowing about it. */
@media (min-width: 901px) {
  .nst-watchgrid {
    flex-direction: row; align-items: stretch;
    max-width: none; margin-inline: 0;
    padding-block: 8px;
  }

  /* Each athlete is a column of their own: the node on its own row and the
     name under it, so the three-column grid the phone uses is not what this
     wants. A fixed width keeps the spacing even whatever the names come to. */
  /* Share the row rather than run off it. Nine athletes at a fixed 200px came
     to 1800px in a 1376px section, so the last two sat outside the page and the
     line to them left the screen -- which is the same complaint the vertical
     path had, turned ninety degrees. They divide the width instead, and the
     basis is a floor rather than a size: SHOW MORE can double the count. */
  .nst-watchcard {
    grid-template-columns: 1fr;
    grid-template-rows: 92px auto;
    flex: 1 1 0; min-width: 96px;
    /* 12px of grid gap plus the row's own slack came to 52px under each face;
       the name belongs directly under its athlete. */
    gap: 0; padding: 22px 0;
    justify-items: center; text-align: center;
  }
  /* The row overrides, not the columns: odd and even swap height rather than
     side, so the line still alternates instead of running flat. */
  .nst-watchcard .nst-watchcard__thread,
  .nst-watchcard:nth-child(even) .nst-watchcard__thread {
    grid-column: 1; grid-row: 1; justify-self: center;
  }
  /* The words ride in the node's own row rather than in a row of their own.

     Given row 2 they all started at the same height while the nodes alternated
     above them, so a high node sat 67px clear of its name and a low one 12 --
     the name looked attached to whichever athlete was nearest, not to its own.
     In the row with the node they drop as it drops. */
  .nst-watchcard .nst-watchcard__body,
  .nst-watchcard:nth-child(even) .nst-watchcard__body {
    grid-column: 1; grid-row: 2; align-self: start;
  }
  /* Each name drops by whatever its own node did: the row is 92px against a
     52px node, so a low node has taken 40px and the words follow it down.

     Level, they all sat on one baseline while the nodes alternated above them,
     which left a high node 67px clear of its own name and its neighbour 12 --
     each name read as belonging to whichever athlete was nearest. */
  .nst-watchcard:nth-child(even) .nst-watchcard__body { padding-top: 40px; }
  .nst-watchcard:nth-child(odd) .nst-watchcard__body { padding-top: 0; }
  /* The drop between the two sides. It is what gives the join its lean; level
     nodes would draw a flat rule through the row. */
  .nst-watchcard:nth-child(even) .nst-watchcard__thread { align-items: flex-end; }
  .nst-watchcard:nth-child(odd) .nst-watchcard__thread { align-items: flex-start; }

  .nst-watchcard__tags { justify-content: center; }
}

/* In their words. Every other section on the page is tiled imagery, so this one
   is type on the page -- no cards, no panels. Scale and a hairline do the work
   the boxes were doing, and the words get to be the thing you look at. */
/* The section's own 72px, not 8. This is type on the page rather than a grid
   of cards, and the tighter head it was given for that also pulled it up into
   the section above -- the By sport tiles ran straight into IN THEIR WORDS
   with nothing between them. The head keeps its own spacing below. */
.nst-words { display: block; }
/* The quote runs the full measure and the speaker sits under it. A fixed
   210px byline column beside the text left a wide empty gutter on every short
   quote and pinned all five names to one straight rail. */
/* The rule runs the full width; the words stop at a readable measure inside
   it. Alternating the whole block left a half-width hairline and a column of
   dead space beside every short quote. */
.nst-say { margin: 0; padding: 32px 0 30px; border-top: 1px solid var(--line); display: block; cursor: pointer; }
.nst-say:first-child { border-top: 0; padding-top: 20px; }
.nst-say[hidden] { display: none; }
.nst-say__text { margin: 0; max-width: 68ch; font-family: var(--cond); font-size: clamp(26px, 3vw, 42px); font-weight: 500; line-height: 1.14; letter-spacing: -.005em; text-wrap: pretty; transition: color .2s ease; }
.nst-say:hover .nst-say__text { color: var(--orange); }
/* The first quote is the loudest; the rest step back so the section reads as a
   column rather than five equal shouts. */
/* Wide, like the quotes under it. Capping the lead at 26ch stacked it three
   lines deep and left two thirds of the row empty beside it, which read as a
   column that had run out rather than a pull quote. */
.nst-say:first-child .nst-say__text { font-size: clamp(30px, 3.4vw, 48px); max-width: min(46ch, 100%); }
/* The speaker, under their own words. The face is set large enough to be a
   person rather than a bullet beside 42px type. */
.nst-say__by { display: flex; align-items: center; gap: 14px; padding-top: 18px; }
.nst-say__by .nst-avatar { flex-shrink: 0; width: 46px; height: 46px; font-size: 16px; }
.nst-say:first-child .nst-say__by .nst-avatar { width: 56px; height: 56px; font-size: 19px; }
.nst-say__lines { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.nst-say__who { font-family: var(--cond); font-size: 20px; font-weight: 700; text-transform: uppercase; line-height: 1; }
.nst-say:first-child .nst-say__who { font-size: 24px; }
.nst-say:hover .nst-say__who { color: var(--orange); }
.nst-say__meta { font-family: var(--mono); font-size: 9px; letter-spacing: .16em; color: var(--dim); }

/* Most watched: one large player, four supporting tiles. */
.nst-watched { display: grid; grid-template-columns: 1.5fr 1fr 1fr; gap: 14px; padding-top: 14px; }
.nst-watched__col { display: flex; flex-direction: column; gap: 14px; }
.nst-watched__lead { position: relative; height: 460px; background: var(--hatch); display: grid; place-items: center; overflow: hidden;  border-radius: var(--r); }
.nst-watched__tile { position: relative; flex: 1; min-height: 229px; background: var(--hatch); overflow: hidden;  border-radius: var(--r); }
.nst-watched__lead img, .nst-watched__tile img { position: absolute; inset: 0; }
/* Runtime, top right -- where the play badge used to be. It is the one thing a
   still cannot tell you, and the tile already opens the clip without a mark
   saying so. */
.nst-watched__time {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
  color: #fff; background: rgba(8,8,10,.72); padding: 4px 8px;
  border-radius: var(--r-sm);
}
.nst-watched__time[hidden] { display: none; }
.nst-watched__rank { position: absolute; top: 16px; left: 18px; font-family: var(--mono); font-size: 11px; letter-spacing: .16em; color: var(--ink); background: var(--orange); padding: 5px 9px; font-weight: 700; z-index: 2; }
.nst-watched__tile .nst-watched__rank { top: 14px; left: 16px; font-size: 10px; color: var(--muted); background: none; padding: 0; font-weight: 400; }
.nst-watched__body { position: absolute; left: 26px; bottom: 26px; right: 26px; z-index: 2; }
.nst-watched__tile .nst-watched__body { left: 20px; bottom: 20px; right: 20px; }
.nst-watched__title { font-family: var(--cond); font-size: 44px; font-weight: 700; text-transform: uppercase; line-height: 1; padding-top: 8px; }
.nst-watched__tile .nst-watched__title { font-size: 28px; line-height: 1.02; padding-top: 6px; }

/* Under a minute: portrait rail. */
.nst-section--bleed { padding-left: 0; padding-right: 0; }
.nst-section__head--inset { margin: 0 var(--gutter); }
.nst-rail { display: flex; gap: 14px; padding: 14px var(--gutter) 0; overflow-x: auto; scrollbar-width: none; }
.nst-rail::-webkit-scrollbar { display: none; }
.nst-short { position: relative; width: 238px; height: 420px; flex-shrink: 0; background: var(--hatch); overflow: hidden;  border-radius: var(--r); }
.nst-short img { position: absolute; inset: 0; }
.nst-short__body { position: absolute; left: 16px; bottom: 16px; right: 16px; z-index: 2; }
.nst-short__time { position: absolute; top: 12px; right: 12px; z-index: 2; padding: 3px 7px; border-radius: var(--r-sm); background: rgba(8,8,10,.86); font-family: var(--mono); font-size: 10px; font-weight: 500; color: var(--text); }
.nst-short__title { font-family: var(--cond); font-size: 22px; font-weight: 600; text-transform: uppercase; line-height: 1.04; padding-top: 5px; }

/* Classroom: stat panels, the first one accented. */
.nst-figure { background: var(--card); padding: 26px;  border-radius: var(--r); }
.nst-figure--lead { border-top: 2px solid var(--orange); }
.nst-figure__value { font-family: var(--cond); font-size: 58px; font-weight: 700; line-height: 1; }
.nst-figure--lead .nst-figure__value { color: var(--orange); }
.nst-figure__label { font-family: var(--mono); font-size: 10px; letter-spacing: .16em; color: var(--dim); padding-top: 6px; text-transform: uppercase; }

.nst-foot { display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; padding: 22px var(--gutter); border-top: 1px solid var(--line); margin-top: 72px; font-family: var(--mono); font-size: 11px; letter-spacing: .16em; color: var(--dim); }
/* The line under the wordmark, set in the body face rather than the mono the
   rest of the footer uses: it is a sentence, not a data point. */
.nst-foot__brand { display: flex; flex-direction: column; gap: 6px; }
.nst-foot__line { font-family: 'Barlow', sans-serif; font-size: 13px; letter-spacing: 0; text-transform: none; color: var(--muted); }
.nst-foot__legal { display: flex; gap: 22px; }
.nst-foot__legal a { color: var(--dim); text-transform: uppercase; }
.nst-foot__legal a:hover { color: var(--text); }

/* Reels: two rows of three 9:16 frames. Life is shot on phones, so the wide
   landscape tile cropped most of every frame away. */
/* By sport: cards carrying a moment from that sport and the faces behind it.
   A bar chart in the middle of a page of film reads as a spreadsheet -- the
   numbers are still here, they just sit on something. */
/* One row, scrolled sideways. Four to a row wrapped every sport past the fourth
   onto a second line, which left a ragged end -- seven sports meant an empty
   cell, patched by running the fourth card down both rows -- and made the block
   two cards deep for no gain. On one line the sports read as a set you move
   along, and however many there are the section stays one card deep. */
.nst-sports {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(210px, 1fr);
  gap: 8px; padding-top: 14px;
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: thin; scrollbar-color: var(--edge) transparent;
  scroll-snap-type: x proximity;
}
.nst-sports::-webkit-scrollbar { height: 6px; }
.nst-sports::-webkit-scrollbar-thumb { background: var(--edge); border-radius: 999px; }
.nst-sports::-webkit-scrollbar-track { background: transparent; }
.nst-sportcard { position: relative; aspect-ratio: 5 / 4; overflow: hidden; border-radius: var(--r); background: var(--hatch); scroll-snap-align: start; cursor: pointer; }
.nst-sportcard:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
/* Equal cards, four to a row. The sport's own numbers carry the weight rather
   than its tile size. */
.nst-sportcard--lead { grid-column: span 1; grid-row: span 1; }
/* Seven sports leave one cell empty at the end of the second row. The fourth
   card takes the spare by running down both rows, so the block stays a
   rectangle without making any one sport look more important. */
.nst-sportcard img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .55s cubic-bezier(.22,.61,.36,1); }
.nst-sportcard:hover img { transform: scale(1.05); }
.nst-sportcard__scrim { position: absolute; inset: 0; background: linear-gradient(#0000 30%, rgba(8,8,10,.9)); }
.nst-sportcard__body { position: absolute; inset: auto 0 0 0; z-index: 2; padding: 18px; display: flex; flex-direction: column; gap: 8px; }
.nst-sportcard__name { font-family: var(--cond); font-size: 30px; font-weight: 700; text-transform: uppercase; line-height: 1; }
.nst-sportcard--lead .nst-sportcard__name { font-size: 30px; }
.nst-sportcard__stat { display: flex; align-items: baseline; gap: 6px; font-family: var(--mono); font-size: 9px; letter-spacing: .14em; color: var(--dim); text-transform: uppercase; }
.nst-sportcard__num { font-family: var(--cond); font-size: 20px; font-weight: 700; color: var(--orange); letter-spacing: 0; }
.nst-sportcard--lead .nst-sportcard__num { font-size: 20px; }
.nst-sportcard__dot { color: var(--faint); }
.nst-sportcard__faces { display: flex; align-items: center; gap: 5px; padding-top: 2px; }
.nst-sportcard__faces .nst-avatar { border-color: rgba(244,244,242,.22); }
.nst-sportcard__more { font-family: var(--mono); font-size: 9px; color: var(--dim); padding-left: 2px; }

.nst-reels { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding-top: 14px; }
.nst-reel { position: relative; aspect-ratio: 9 / 14; overflow: hidden; border-radius: var(--r); background: var(--hatch); }
.nst-reel img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .5s cubic-bezier(.22,.61,.36,1); }
.nst-reel:hover img { transform: scale(1.04); }
.nst-reel__scrim { position: absolute; inset: 0; background: linear-gradient(#0000 42%, rgba(8,8,10,.92)); }
.nst-reel__play { position: absolute; top: 14px; right: 14px; z-index: 2; width: 30px; height: 30px; display: grid; place-items: center; border-radius: 50%; background: rgba(8,8,10,.7); color: var(--orange); font-size: 11px; padding-left: 2px; }
.nst-reel__body { position: absolute; inset: auto 0 0 0; z-index: 2; padding: 18px; display: flex; flex-direction: column; gap: 8px; }
.nst-reel__title { font-family: var(--cond); font-size: 24px; font-weight: 700; text-transform: uppercase; line-height: 1.04; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.nst-reel__who { display: flex; align-items: center; gap: 8px; }
.nst-reel__name { font-family: var(--mono); font-size: 9px; letter-spacing: .14em; color: var(--muted); text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- player --------------------------------------------------------------- */

.nst-locked { overflow: hidden; }
.nst-player { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center; padding: 4vh 4vw; }
.nst-player[hidden] { display: none; }
.nst-player__scrim { position: absolute; inset: 0; background: rgba(4, 4, 6, .88); backdrop-filter: blur(4px); }
.nst-player__box { position: relative; width: min(1080px, 100%); max-height: 92vh; overflow: auto; background: var(--card); border-radius: var(--r); }
.nst-player__stage { position: relative; aspect-ratio: 16 / 9; background: #000; border-radius: var(--r) var(--r) 0 0; overflow: hidden; }
/* Media fills the stage; the transport is excluded so it can pin to the bottom
   rather than being stretched over the whole frame. */
.nst-player__stage > :not(.nst-transport) { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.nst-player__stage video { object-fit: contain; background: #000; }
/* Stills are usually portrait; contain keeps the whole frame visible. */
.nst-player__still { object-fit: contain !important; }
.nst-player__x { position: absolute; top: 12px; right: 12px; z-index: 3; width: 38px; height: 38px; border: 0; border-radius: var(--r-sm); background: rgba(8, 8, 10, .7); color: var(--text); font-size: 16px; cursor: pointer; }
.nst-player__x:hover { background: var(--orange); color: var(--ink); }
.nst-player__away { position: absolute; inset: auto 0 20px 0; z-index: 3; width: fit-content; margin: 0 auto; padding: 12px 22px; border-radius: var(--r-sm); background: var(--orange); color: var(--ink); font-family: var(--cond); font-size: 14px; letter-spacing: .16em; font-weight: 600; }
/* Overscale the embed so YouTube's own bars fall outside the stage, the same
   crop the hero uses. */
.nst-player__stage.is-cropped iframe {
  /* Clear every side explicitly: the stage's base rule pins inset:0, and
     inset:auto does not override the individual longhands -- leaving them set
     dragged the frame into the corner. */
  top: 50% !important; left: 50% !important; right: auto !important; bottom: auto !important;
  width: 122%; height: 122%; transform: translate(-50%, -50%); pointer-events: none;
}

.nst-transport { position: absolute; inset: auto 0 0 0; z-index: 4; display: flex; align-items: center; gap: 14px; padding: 14px 18px; background: linear-gradient(#0000, rgba(8,8,10,.92)); }
.nst-transport__btn { width: 34px; height: 34px; flex-shrink: 0; border: 0; border-radius: 50%; background: rgba(244,244,242,.12); color: var(--text); font-size: 13px; cursor: pointer; }
/* Behind a hover query for the same reason as the docked bar: a tap sticks in
   :hover, and orange-on-orange hides the icon. */
@media (hover: hover) {
  .nst-transport__btn:hover { background: var(--orange); color: var(--ink); }
}
/* The sound control carries a word, not a speaker glyph: there is no monochrome
   speaker in Unicode that survives iOS, and the emoji one is a colour sticker
   sitting on a dark player. A word also says which way the switch throws. */
.nst-transport__btn--sound {
  width: auto; height: 30px; padding: 0 12px; border-radius: var(--r-pill);
  font-family: var(--mono); font-size: 9px; letter-spacing: .14em;
}
/* The button is only ever given "nst-transport__btn is-live" -- there is no
   --sound modifier in the markup, so a rule needing one never applied and the
   unmuted state was whatever :hover had left behind. Tinted rather than filled,
   so the icon stays legible against it. */
.nst-transport__btn.is-live,
.nst-transport__btn.is-live:hover { background: rgba(255,74,21,.16); color: var(--orange); }
.nst-transport__time { font-family: var(--mono); font-size: 11px; color: var(--muted); flex-shrink: 0; min-width: 76px; }
.nst-transport__track { flex: 1; height: 4px; border-radius: 999px; background: rgba(244,244,242,.2); cursor: pointer; }
/* The embed reports its position on a 500ms poll, so the fill arrives in steps.
   Easing it over slightly longer than that interval turns the jumps into a bar
   that keeps moving between reports -- linear, because any curve reads as the
   clip speeding up and slowing down. */
.nst-transport__track > span { display: block; width: 0; height: 100%; border-radius: 999px; background: var(--orange); transition: width .55s linear; }
/* A seek should land, not glide across the bar from wherever it was. And a file
   reports through timeupdate, which fires every frame -- easing that would leave
   the bar permanently half a second behind the picture. */
.nst-transport__track.is-seeking > span,
.nst-transport__track.is-live > span { transition: none; }

.nst-player__meta { padding: 24px 28px 28px; }
/* Byline: face, then the name as a button into the athlete's page. The button
   is stripped back to text so the line still reads as a kicker, not a control,
   until you are on it. */
/* The gap supplies the space between the name and the date, so the separator
   itself carries none -- otherwise the line read "WOODSON JR.· JAN 31". */
.nst-player__by { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; }
.nst-player__who {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit;
  color: var(--accent);
}
.nst-player__who:hover, .nst-player__who:focus-visible { text-decoration: underline; }
.nst-player__who:disabled { cursor: default; text-decoration: none; }
.nst-player__title { font-family: var(--cond); font-size: 38px; font-weight: 700; text-transform: uppercase; line-height: 1.02; padding-top: 8px; }
.nst-player__caption { margin: 14px 0 0; font-size: 16px; line-height: 1.55; color: var(--muted); white-space: pre-wrap; }

/* The replies, under the caption. The hero runs them over the clip while it
   plays; that needs a column of empty screen beside the film, which a phone does
   not have -- so the player holds the whole conversation instead, read at your
   own pace rather than paced by the video. */
.nst-player__talk { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line); }
.nst-player__talk[hidden] { display: none; }
.nst-player__talk-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; padding-bottom: 14px; }
.nst-player__talk-label { font-family: var(--mono); font-size: 10px; letter-spacing: .18em; color: var(--orange); }
.nst-player__talk-note { font-family: var(--mono); font-size: 9px; letter-spacing: .14em; color: var(--faint); }

/* Replies read as things people said, not rows in a table. A rule under every
   one turned fifteen short remarks into a ledger -- more dividers than text,
   and each line boxed in by two of them.

   Each is its own small surface instead, with the gap between them doing the
   separating. No borders at all: the shape and the space are enough to tell one
   remark from the next, and the eye can move down a column of them. */
.nst-player__comments { display: flex; flex-direction: column; gap: 8px; }
.nst-player__comment {
  padding: 12px 14px; background: rgba(244,244,242,.045);
  border-radius: var(--r-sm); border-top-left-radius: 3px;
}
/* The quote marks are the JS's; the type carries the voice. */
.nst-player__comment-text { font-size: 15px; line-height: 1.45; color: var(--text); }
.nst-player__comment-by {
  padding-top: 6px; font-family: var(--mono); font-size: 9px;
  letter-spacing: .1em; color: var(--dim); text-transform: lowercase;
}

/* The comment count on the hero is a way in, not a statistic -- it opens the
   player where the replies are. Underlined so it reads as the one pressable
   thing on a line of figures. */
.nst-hero__talk {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; letter-spacing: inherit; color: inherit; text-transform: inherit;
  text-decoration: underline; text-underline-offset: 3px;
  text-decoration-color: rgba(244,244,242,.35);
  /* A flex row, so the gap between the number and its label has to be a gap --
     a space in the markup between two flex items is collapsed away, and the
     line read "15COMMENTS". Plays and likes get theirs from being separate
     spans on the parent's own gap. */
  display: inline-flex; align-items: baseline; gap: .34em;
}
.nst-hero__talk:hover { color: var(--text); text-decoration-color: var(--orange); }
/* The counts line is pointer-events:none so it never blocks the clip behind it.
   The reply count is the one thing on it that can be pressed, so it takes them
   back -- without this the button is drawn but dead at every width. */
.nst-hero__talk { pointer-events: auto; }

/* Opening the player.

   The sheet used to appear on a black stage and the picture popped in whenever
   it decoded -- with the transport sometimes drawing over the top of nothing.
   The clip fades up instead, over its own poster, so the frame is filled from
   the moment the sheet lands and the only change is the still becoming footage.

   The overlay itself fades rather than cutting in, which stops the page behind
   it snapping to black. */
.nst-player:not([hidden]) { animation: nst-player-in .18s ease-out both; }
@keyframes nst-player-in { from { opacity: 0; } to { opacity: 1; } }
.nst-player__stage video.is-arriving { opacity: 0; }
.nst-player__stage video { transition: opacity .22s ease; }
/* The poster sits behind the clip, so the stage is never an empty black box --
   it is the still from the card you just tapped, which is what makes the open
   read as continuous rather than as a load.

   Blurred and pushed back, or it is not a backdrop -- it is the same picture
   again. A portrait photo in a wide stage leaves bars top and bottom, and at
   full strength the fill read as a second, zoomed copy of the image sitting
   behind the first: one photograph shown twice, cropped differently.

   The blur is what makes it a wash rather than a duplicate. Scaled up past the
   edges too, since blurring to the frame's edge leaves the smear of a hard
   border inside it. */
/* The stage carries the poster as an inline background-image but must not draw
   it: at 0/auto it paints nothing itself and hands the image to ::before, which
   is the only thing that renders it. Letting both paint put an unblurred copy
   under the blurred one. */
.nst-player__stage { background-size: 0 0; }
.nst-player__stage::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background-image: inherit;
  background-size: cover;
  background-position: center;
  filter: blur(28px) saturate(1.15) brightness(.55);
  transform: scale(1.12);
  pointer-events: none;
}
/* Everything real sits over the wash. */
.nst-player__stage > * { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) {
  .nst-player:not([hidden]) { animation: none; }
  .nst-player__stage video { transition: none; }
  .nst-player__stage video.is-arriving { opacity: 1; }
}

/* Transport icons.

   Drawn as inline SVG rather than set as Unicode: iOS promotes the play and
   pause glyphs to colour emoji, and the speaker characters have no monochrome
   form in any font -- the bar was a mix of cartoon stickers and a word. These
   take the button's own colour and sit on the optical centre of it. */
.nst-playing__btn svg, .nst-transport__btn svg, .nst-player__x svg {
  width: 56%; height: 56%; display: block; margin: auto;
}
.nst-playing__btn, .nst-transport__btn, .nst-player__x {
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 0; padding: 0;
}
/* Play sits a shade right of centre -- a triangle's mass is left of its box. */
.nst-playing__btn svg path[d^="M8 5.5"], .nst-transport__btn svg path[d^="M8 5.5"] {
  transform: translateX(.6px);
}
/* The WATCH button's mark is type-sized and sits on the label's baseline. */
.nst-btn--orange svg { width: 1em; height: 1em; flex: 0 0 auto; }
/* On the tiles it fills the round mark it sits in. */
.nst-watchtile__play svg { width: 46%; height: 46%; }

/* Anything that opens the player reads as pressable. */
.is-playable { cursor: pointer; }
.is-playable:hover .nst-card__title,
.is-playable:hover .nst-watched__title,
.is-playable:hover .nst-upnext__title,
.is-playable:hover .nst-short__title { color: var(--orange); }
/* No play badge. Every one of these surfaces opens its clip when tapped, so a
   mark in the corner only restated what the whole tile already promises -- and
   as a "▶" character it was one iOS promotes to a colour emoji, which is what
   made it read as a sticker laid on the photograph. What the picture cannot say
   is how long the clip runs, and that is what sits there now (.nst-watched__time
   and .nst-watchtile__time). */
/* On the thumbnail, not the row. Pinned to the row's right edge the badge sat
   on top of a two-line headline and clipped the words behind it. */
/* No play badge where a runtime already sits: it says the same thing (this is
   video) and also tells you how long it is. Both were pinned to the same top
   right corner, so the badge covered the figure. */
.nst-upnext__item.has-video::after,
.nst-short.has-video::after { content: none; }

.nst-empty { padding: 80px var(--gutter); text-align: center; color: var(--dim); font-family: var(--mono); font-size: 13px; letter-spacing: .12em; }
.nst-empty__hint { padding-top: 10px; color: var(--faint); font-size: 11px; }
.nst-section[hidden], .nst-empty[hidden] { display: none; }

/* --- reflow --------------------------------------------------------------- */

@media (max-width: 1180px) {
  .nst-hero { grid-template-columns: 1fr; }
  .nst-upnext { border-left: 0; border-top: 1px solid var(--line); }
  .nst-grid--4 { grid-template-columns: repeat(3, 1fr); }
  .nst-watched { grid-template-columns: 1fr 1fr; }
  .nst-hero__stage { height: 560px; }
}

@media (max-width: 900px) {
  :root { --gutter: 20px; }
  .nst-head { grid-template-columns: 1fr; gap: 14px; }
  .nst-head__actions { justify-content: flex-start; }
  /* A short placeholder rather than none. It was hidden because the desktop
     copy -- "Ask the network anything — athletes, offers, schools, moments" --
     does not fit, and the box was a button into the sheet anyway. It is a
     field you type in on a phone now, so it says so; the shorter line is set
     on the element in the template. */
  .nst-ask__input::placeholder { color: var(--label-dim); }
  /* No ⌘K on a phone, and the space it takes is the field's. */
  .nst-ask__key { display: none; }
  /* Nor the label. The star already says what the field is, and the 85px the
     label takes is what the placeholder needs to say "Find an app — ..." the
     way the desktop does, instead of the question on its own. */
  .nst-ask__label { display: none; }
  .nst-stats { grid-template-columns: repeat(2, 1fr); }
  .nst-stat:nth-child(2) { border-right: 0; }
  .nst-lead, .nst-quotes { grid-template-columns: 1fr; }
  .nst-grid--3, .nst-grid--4, /* By sport: cards carrying a moment from that sport and the faces behind it.
   A bar chart in the middle of a page of film reads as a spreadsheet -- the
   numbers are still here, they just sit on something. */
/* One row, scrolled sideways. Four to a row wrapped every sport past the fourth
   onto a second line, which left a ragged end -- seven sports meant an empty
   cell, patched by running the fourth card down both rows -- and made the block
   two cards deep for no gain. On one line the sports read as a set you move
   along, and however many there are the section stays one card deep. */
.nst-sports {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(210px, 1fr);
  gap: 8px; padding-top: 14px;
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: thin; scrollbar-color: var(--edge) transparent;
  scroll-snap-type: x proximity;
}
.nst-sports::-webkit-scrollbar { height: 6px; }
.nst-sports::-webkit-scrollbar-thumb { background: var(--edge); border-radius: 999px; }
.nst-sports::-webkit-scrollbar-track { background: transparent; }
.nst-sportcard { position: relative; aspect-ratio: 5 / 4; overflow: hidden; border-radius: var(--r); background: var(--hatch); scroll-snap-align: start; cursor: pointer; }
.nst-sportcard:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
/* Equal cards, four to a row. The sport's own numbers carry the weight rather
   than its tile size. */
.nst-sportcard--lead { grid-column: span 1; grid-row: span 1; }
/* Seven sports leave one cell empty at the end of the second row. The fourth
   card takes the spare by running down both rows, so the block stays a
   rectangle without making any one sport look more important. */
.nst-sportcard img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .55s cubic-bezier(.22,.61,.36,1); }
.nst-sportcard:hover img { transform: scale(1.05); }
.nst-sportcard__scrim { position: absolute; inset: 0; background: linear-gradient(#0000 30%, rgba(8,8,10,.9)); }
.nst-sportcard__body { position: absolute; inset: auto 0 0 0; z-index: 2; padding: 18px; display: flex; flex-direction: column; gap: 8px; }
.nst-sportcard__name { font-family: var(--cond); font-size: 30px; font-weight: 700; text-transform: uppercase; line-height: 1; }
.nst-sportcard--lead .nst-sportcard__name { font-size: 30px; }
.nst-sportcard__stat { display: flex; align-items: baseline; gap: 6px; font-family: var(--mono); font-size: 9px; letter-spacing: .14em; color: var(--dim); text-transform: uppercase; }
.nst-sportcard__num { font-family: var(--cond); font-size: 20px; font-weight: 700; color: var(--orange); letter-spacing: 0; }
.nst-sportcard--lead .nst-sportcard__num { font-size: 20px; }
.nst-sportcard__dot { color: var(--faint); }
.nst-sportcard__faces { display: flex; align-items: center; gap: 5px; padding-top: 2px; }
.nst-sportcard__faces .nst-avatar { border-color: rgba(244,244,242,.22); }
.nst-sportcard__more { font-family: var(--mono); font-size: 9px; color: var(--dim); padding-left: 2px; }

.nst-reels { grid-template-columns: repeat(2, 1fr); }
  .nst-watched { grid-template-columns: 1fr; }
  /* No room to indent every other quote on a narrow screen. */
  .nst-say, .nst-say:nth-child(even) { max-width: none; margin-left: 0; }
  .nst-say__by { gap: 11px; padding-top: 2px; }
  .nst-say__by .nst-avatar,
  .nst-say:first-child .nst-say__by .nst-avatar { width: 38px; height: 38px; font-size: 14px; }
  .nst-say__lines { flex-direction: row; align-items: baseline; gap: 10px; }
  .nst-table--sport { grid-template-columns: 40px 1fr 90px 90px; }
  .nst-table--sport > :nth-child(3) { display: none; }
  /* Athletes to watch is one path at every width, so it has no column count
     to drop here any more. */
  .nst-watched__lead { height: 320px; }
  .nst-section__head { flex-wrap: wrap; gap: 10px; }
  .nst-section__head h2, .nst-display { font-size: 40px; }
  .nst-hero__body { left: var(--gutter); right: var(--gutter); bottom: 40px; width: auto; }
  .nst-nav { overflow-x: auto; }
  .nst-nav__meta { display: none; }
}

@media (max-width: 560px) {
  .nst-grid--3, .nst-grid--4, .nst-stats { grid-template-columns: 1fr; }
  /* Still one row here, just narrower cards -- the sports scroll sideways at
     every width now, so there is no column count to reduce. The rules that used
     to undo the seven-sport two-row trick went with the trick itself. */
  .nst-sports { grid-auto-columns: minmax(160px, 1fr); }
  .nst-sportcard__body { padding: 2px; }
  .nst-sportcard__name, .nst-sportcard--lead .nst-sportcard__name { font-size: 24px; }

  .nst-reels { grid-template-columns: repeat(2, 1fr); gap: 6px; }
  .nst-stat { border-right: 0; }
  .nst-card, .nst-grid--4 .nst-card { height: 340px; }
  .nst-hero__stage { height: 440px; }
}

/* --- phone ----------------------------------------------------------------

   Below this the page stops being a scaled-down desktop layout. Three things
   change in kind rather than degree: the header becomes a single sticky band
   instead of three stacked rows, the hero fills the screen instead of sitting
   in a box, and the transport bar moves to the bottom of the viewport, since
   there is no longer a full-width strip under the hero for it to live in.

   Everything else is the same page at phone measure. */


/* "LOG IN" and "+ CREATE NIL APP" side by side need ~250px of label before
   padding; a 390px screen has already spent 16px of gutter and given the brand
   the rest of the line. The call to action keeps the verb and drops the noun --
   the page it lands on says the rest. */


/* Tap targets. The comp's chips and buttons are mouse-sized; on a touch screen
   they need to be reachable with a thumb. */


/* --- Watch ----------------------------------------------------------------

   Every clip on the network, three across. No posters by design: half of these
   moments carry no still, and a grid where some tiles have art and some do not
   reads as broken rather than sparse. So the tile is type on a surface -- the
   headline is the picture, and the play mark says what it opens. */

.nst-watchview[hidden] { display: none; }
.nst-watchview__meta { font-family: var(--mono); font-size: 11px; letter-spacing: .16em; color: var(--faint); white-space: nowrap; }

/* Was a glass panel -- a translucent, rounded, lit-edged surface floating over
   the page. It read as a control from another product sitting on top of this
   one. Flat now: a panel cut out of the page, marked by its own surface and a
   rule, the way every other block here is. The class name stays because the
   markup and the JS both use it. */
.nst-glasspane {
  background: var(--card);
  border: 1px solid var(--line);
}

/* Player left, queue right -- the comp's shape. Both columns stretch so the
   queue ends level with the player instead of running past it. */
/* Player left, queue right. The columns start together; the queue's height is
   set from the player's in JS -- a pure-CSS stretch is circular here, because
   the queue would be sizing the very row it is trying to match. */
/* Player left, queue right. Both columns stretch so the queue ends on the same
   line as the player's byline: with align-items:start the queue was its own
   content height and ran 31px past the stage, which read as the rail being
   aligned to the athlete's app block rather than to the column. */
/* Player left, queue right. The queue is a drawn panel -- surface plus border --
   so it cannot be stretched to the player's height: the leftover shows as a
   band of empty panel under the last row, with the bottom edge floating past
   the athlete card. It sizes to its own rows and scrolls when there are more
   of them than the player is tall. */
/* The clip takes the screen, and what is next runs along the bottom.

   It was a 16:9 frame in a column with a 396px list of titles beside it: the
   video held about half the width and the rest of the view was text about
   other videos. Stacked instead, the stage is the whole width and the queue
   becomes a strip of thumbnails under it -- the clips still there to pick
   from, but as frames rather than as a table of contents. */
.nst-watchview__lead { display: flex; flex-direction: column; gap: 0; padding: 0; }
/* An explicit display beats the hidden attribute, which only ever works by
   setting display: none -- so the stage stayed on screen when it was hidden,
   showing an empty frame above the grid. Both of these declare a display, so
   both have to say what hidden means for them. */
.nst-watchview__lead[hidden] { display: none; }

/* Edge to edge, and as tall as the screen will give it. Capped at 78vh so the
   title and the athlete's row stay on screen under it -- a full 100vh stage
   pushes everything about the clip below the fold, and the reader has to
   scroll to find out who they are watching. The border goes with the gutter:
   a frame drawn around something that reaches both edges has nothing to sit
   against. */
.nst-wstage__frame {
  position: relative; aspect-ratio: 16 / 9; max-height: 78vh;
  overflow: hidden; background: #000; border: 0;
  width: 100%;
}
/* Everything but the transport fills the frame -- the bar positions itself. */
.nst-wstage__frame > :not(.nst-transport) { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; object-fit: cover; }
/* The poster sits over the embed until playback starts, so YouTube's loading
   state never shows through. */
.nst-wstage__cover { z-index: 2; transition: opacity .5s ease; }
.nst-wstage__cover.is-gone { opacity: 0; pointer-events: none; }

/* Portrait clip in a landscape stage. The clip keeps its own ratio and a
   blurred, overscaled copy fills the width behind it, rather than cropping to
   a middle sliver and losing the athlete.

   The frame stays 16:9 and does not follow the clip. Letting it go 9:16 makes
   the stage taller than the screen and pushes the byline under the fold, and
   the row of tiles beside it has to match a frame that changes shape with
   whatever is playing. The blur is what makes a reel sit in a wide frame. */
.nst-wstage__frame.is-portrait video:not(.nst-wstage__mirror) { object-fit: contain; z-index: 1; }
.nst-wstage__mirror { z-index: 0; object-fit: cover; filter: blur(38px) saturate(140%) brightness(.55); transform: scale(1.25); }

/* Overscale the embed so YouTube's logo and share button fall outside the
   stage. modestbranding no longer suppresses them, so this is what is left. */
.nst-wstage__frame.is-cropped iframe {
  /* Every side cleared explicitly: the frame's base rule pins inset:0, and the
     longhands would otherwise drag the embed into the corner. */
  top: 50% !important; left: 50% !important; right: auto !important; bottom: auto !important;
  width: 124%; height: 124%; transform: translate(-50%, -50%); pointer-events: none;
}
/* The stage runs to both edges now, so everything written about the clip
   takes the page's gutter back for itself. */
/* No indent any more: the way out moved to the right-hand corner, so nothing
   overlaps where the headline starts. */
.nst-wstage__title { margin: 20px 0 0; padding: 0 var(--gutter); font-family: var(--cond); font-size: 42px; font-weight: 700; line-height: 1; text-transform: uppercase; max-width: 1080px; }
.nst-wstage__meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 12px var(--gutter) 0; font-family: var(--mono); font-size: 11px; letter-spacing: .1em; color: #9a9aa3; text-transform: uppercase; }
.nst-wstage__dot { color: var(--faint); }

.nst-wstage__by { display: flex; align-items: center; gap: 16px; margin: 18px var(--gutter) 0; padding: 16px 20px; }
.nst-wstage__who { min-width: 0; flex: 1; }
.nst-wstage__name { display: block; padding: 0; border: 0; background: none; cursor: pointer; font-family: var(--cond); font-size: 26px; font-weight: 700; line-height: 1; text-transform: uppercase; color: var(--text); text-align: left; }
.nst-wstage__name:hover { color: var(--orange); }
.nst-wstage__line { padding-top: 5px; font-family: var(--mono); font-size: 10px; letter-spacing: .12em; color: var(--label); }
.nst-wstage__line:empty { display: none; }
.nst-wstage__app { flex-shrink: 0; }

/* The queue. Rows, not tiles -- it is a running order, and it is not in a box:
   a panel here draws a bottom edge that has to end level with something, and
   the player column beside it is a different height for every clip. Without one
   the list simply runs as long as it is and the page scrolls. */
/* A rail of frames along the bottom rather than a column of titles beside the
   clip. Scrolls sideways, so however many are queued it costs one row of the
   screen instead of a third of its width. */
.nst-wqueue { display: flex; flex-direction: column; padding-top: 14px; min-width: 0; }
.nst-wqueue__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 0 var(--gutter) 10px; }
.nst-wqueue__count { font-family: var(--mono); font-size: 10px; letter-spacing: .1em; color: #7a7a84; white-space: nowrap; }
/* The rail itself. The queue's rows are its children, so this is what turns a
   list into a strip. */
/* Fills the width, however many are queued. auto-fill with a floor rather
   than a fixed count: a handful of clips come out large, a full network's
   worth wraps to a second row instead of shrinking to nothing. */
.nst-wqueue > [data-watch-queue] {
  display: grid; gap: 10px; padding: 0 var(--gutter) 44px;
  /* auto-fit, not auto-fill: with a handful of clips auto-fill leaves the
     empty tracks standing and the cards huddle at the left edge, which is what
     left two thirds of the row bare. auto-fit collapses them so what is there
     stretches across. */
  /* A floor and a ceiling. Left to stretch, three clips on a wide screen came
     out 452px across and the cards turned landscape -- the opposite of the
     standing frames these are meant to be. Capped, they stay portrait and the
     row simply holds as many as the width allows. */
  grid-template-columns: repeat(auto-fit, minmax(200px, 260px));
  justify-content: start;
}
.nst-wqueue > [data-watch-queue]::-webkit-scrollbar { height: 6px; }
.nst-wqueue > [data-watch-queue]::-webkit-scrollbar-thumb { background: var(--edge-strong); border-radius: 999px; }

/* Each one is its frame with a line under it, not a row of text with a
   thumbnail attached. */
/* Portrait frames running across. Most of what the network posts is shot on a
   phone, so a 9:16 thumbnail is the clip's own shape rather than a letterboxed
   crop of it -- and standing them up fits far more across the width than a row
   of 16:9 stills, which left half the screen empty. */
/* One card, not a frame with a caption loose underneath. The whole tile is the
   poster and the words sit on it, so a row of them reads as a row of clips
   rather than a filmstrip with a list of titles hanging off the bottom. */
/* Standing frames with real height under the clip, rather than a strip of
   stamps clustered at one edge. */
/* Standing frames with real height under the clip, rather than a strip of
   stamps clustered at one edge. The height comes from the row, not from a
   minimum on the card -- a min-height taller than its own track made every
   card overrun the one beside it. */
.nst-wqrow {
  position: relative; display: block; padding: 0; width: 100%;
  /* Held to a height rather than left to the ratio. Stretched across a wide
     screen a 3:4 card came out 600px tall -- taller than the clip it is
     queued behind, which makes the thing you are watching the smaller of the
     two. The frame crops to fill instead. */
  aspect-ratio: 3 / 4;
  cursor: pointer; background: none;
  border-radius: var(--r); overflow: hidden;
  transition: transform .18s ease;
}
.nst-wqrow:hover { transform: translateY(-3px); }
.nst-wqrow:hover { background: none; }
/* What is playing is marked on the frame -- an orange ring rather than the
   inset edge a list row used, which has nothing to sit against here. */
.nst-wqrow.is-playing { background: none; box-shadow: none; }
.nst-wqrow.is-playing { box-shadow: inset 0 0 0 2px var(--orange); }
.nst-wqrow.is-playing .nst-wqrow__title { color: var(--orange); }
/* The running number belongs to a list. In a rail the position is the order
   they sit in. */
.nst-wqrow__n { display: none; }
.nst-wqrow__thumb {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border-radius: 0; overflow: hidden; background: var(--hatch);
}
/* Filled rather than fitted. Some posters arrive already letterboxed -- a
   landscape game clip exported onto a 9:16 canvas with black bars baked in --
   and nothing here can strip those, but cropping to fill at least keeps every
   frame the same shape. */
.nst-wqrow__thumb img { width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .3s ease; }
.nst-wqrow__thumb img.is-loaded { opacity: 1; }
.nst-wqrow__time { position: absolute; right: 6px; top: 6px; bottom: auto; z-index: 3; font-family: var(--mono); font-size: 9px; color: var(--text); background: rgba(6,6,10,.7); padding: 2px 5px; border-radius: var(--r-sm); }
.nst-wqrow__time[hidden] { display: none; }
/* Over the foot of the poster, on a wash dark enough to read against a lit
   gym floor. */
.nst-wqrow__text {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  min-width: 0; display: block; padding: 34px 12px 12px;
  background: linear-gradient(#0000, rgba(6,6,10,.82) 42%, rgba(6,6,10,.95));
}
.nst-wqrow__title { font-family: var(--cond); font-size: 16px; font-weight: 600; line-height: 1.12; text-transform: uppercase; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.nst-wqrow:hover .nst-wqrow__title { color: var(--orange); }
.nst-wqrow__who { display: block; font-size: 12px; color: var(--muted); padding-top: 3px; }
/* Dropped in the rail: three lines under a 168px frame is more type than the
   frame above it, and the date is on the clip's own page when it opens. */
.nst-wqrow__meta { display: none; }

/* The rest of the library, under the player. */
/* The way back out of a clip, held over the stage rather than sitting above
   it. Placed in the flow it scrolled off the top with everything else, and a
   full-bleed clip with the nav gone reads as a page with no way out -- there
   was nothing on screen to say you were inside something. Fixed, it is always
   the thing you reach for.

   Its own backdrop because it sits on the video: a bare label over moving
   footage is legible for about half a second at a time. */
.nst-watchback[hidden] { display: none; }
.nst-watchview__more { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 44px var(--gutter) 0; }
.nst-watchview__more[hidden] { display: none; }

@media (max-width: 1180px) {
  /* The lead stacks at every width now, so it has no columns to collapse. */
  .nst-wstage__title { font-size: 32px; }
}

@media (max-width: 700px) {
  /* Taller share of the screen on a phone, where there is no width to spare
     and the clip is the whole reason for the view. */
  .nst-wstage__frame { max-height: 68vh; }
  /* Two and a bit frames in view, so the rail reads as something that scrolls
     rather than a row that happens to be cut off. */
  .nst-wqrow__title { font-size: 13px; }
}

/* Topic chips. Pills, like the header -- they are the same kind of control. */
.nst-watchview__topics { display: flex; flex-wrap: wrap; gap: 8px; padding: 26px var(--gutter) 0; }
.nst-watchview__topics[hidden] { display: none; }
.nst-topic {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 16px; border: 1px solid #2a2a32; border-radius: var(--r-pill);
  background: var(--well-raised); color: var(--muted); cursor: pointer;
  font-family: var(--cond); font-size: 14px; letter-spacing: .12em; text-transform: uppercase;
  transition: border-color .16s ease, background .16s ease, color .16s ease;
}
.nst-topic:hover { border-color: #3d3d47; color: var(--text); }
.nst-topic.is-on { background: var(--orange); border-color: var(--orange); color: var(--well); }
/* The count is the quieter half of the chip -- and inverts with it when on. */
.nst-topic__n { font-family: var(--mono); font-size: 11px; letter-spacing: .04em; color: var(--faint); }
.nst-topic:hover .nst-topic__n { color: var(--muted); }
.nst-topic.is-on .nst-topic__n { color: rgba(10,10,10,.62); }

.nst-watchview__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; padding: 26px var(--gutter) 0; }
.nst-watchview__grid[hidden] { display: none; }

/* Poster-led. The frame is the tile; the copy sits under it. */
.nst-watchtile {
  display: flex; flex-direction: column;
  background: var(--well-raised); border: 1px solid var(--line); border-radius: var(--r);
  overflow: hidden;
  transition: border-color .16s ease, transform .16s ease;
}
.nst-watchtile:hover { border-color: var(--edge-strong); transform: translateY(-2px); }

/* 16:9, so a grid of clips lines up whatever shape the source was shot in. */
.nst-watchtile__shot { position: relative; aspect-ratio: 16 / 9; background: var(--hatch); overflow: hidden; }
.nst-watchtile__shot img,
.nst-watchtile__shot video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.nst-watchtile__shot img { opacity: 0; transition: opacity .3s ease, transform .5s cubic-bezier(.22,.61,.36,1); }
.nst-watchtile__shot img.is-loaded { opacity: 1; }
.nst-watchtile:hover .nst-watchtile__shot img { transform: scale(1.04); }
/* The hover preview sits over the poster rather than replacing it, so there is
   never a blank frame while the clip decodes. */
.nst-watchtile__preview { z-index: 1; }
.nst-watchtile__scrim { position: absolute; inset: 0; background: linear-gradient(to top, rgba(8,8,10,.85) 0%, rgba(8,8,10,0) 52%); }

/* Over the frame: play mark bottom-left, runtime bottom-right. */
.nst-watchtile__top { position: absolute; inset: auto 0 0 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 12px; }
.nst-watchtile__play { width: 38px; height: 38px; display: grid; place-items: center; border-radius: 50%; background: var(--orange); color: var(--well); font-size: 14px; padding-left: 2px; transition: transform .16s ease; }
.nst-watchtile:hover .nst-watchtile__play { transform: scale(1.1); }
.nst-watchtile__time { font-family: var(--mono); font-size: 11px; letter-spacing: .08em; color: #fff; background: rgba(8,8,10,.72); padding: 4px 8px; border-radius: var(--r-sm); }
.nst-watchtile__time[hidden] { display: none; }

.nst-watchtile__body { display: flex; flex-direction: column; gap: 12px; padding: 16px 18px 18px; }
.nst-watchtile__title { font-family: var(--cond); font-size: 22px; font-weight: 700; line-height: 1.08; text-transform: uppercase; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.nst-watchtile:hover .nst-watchtile__title { color: var(--orange); }

.nst-watchtile__who { display: flex; align-items: center; gap: 9px; margin-top: auto; }
.nst-watchtile__name { font-family: var(--cond); font-size: 15px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nst-watchtile__foot { display: flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: 10px; letter-spacing: .14em; color: var(--faint); text-transform: uppercase; flex-wrap: wrap; }
.nst-watchtile__dot { color: #2f2f38; }

.nst-watchview .nst-more { display: flex; flex-direction: column; align-items: center; padding: 34px var(--gutter) 60px; }
/* Another explicit display, so another one that has to say what hidden means.
   Its 94px of padding sat under the queue while the grid it pages was not even
   on screen. */
.nst-watchview .nst-more[hidden] { display: none; }
.nst-watchview .nst-more__tail { width: 100%; height: 1px; }
.nst-watchview .nst-more__tail[hidden], .nst-watchview .nst-more__none[hidden] { display: none; }
.nst-watchview .nst-more__none { font-family: var(--mono); font-size: 12px; letter-spacing: .14em; color: var(--faint); }

@media (max-width: 1080px) {
  .nst-watchview__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 680px) {
  .nst-watchview__grid { grid-template-columns: 1fr; }
}

/* --- the desktop hero, flattened ------------------------------------------- */

/* Above 760px only, so none of this reaches the phone build -- that hero is
   owned outright by nilsportstech-hero-mobile.css and went through this same
   exercise already.

   Desktop cannot go full-bleed the way the phone did: the hero here is a grid
   with the film in one column and the up-next rail in the other, and the rail
   needs the page's gutter to sit in. So the film keeps its column. What goes is
   everything that draws a card around it -- the rounded corners, the surface
   behind it, and the ruled edge between the film and the rail. */
@media (min-width: 761px) {
  .nst-hero {
    /* Rounded again. Squaring it was the point of the flatten pass, but on the
       page it read as film running into the edges rather than a frame holding
       it -- and the rail beside it keeps its corners, so the two sat oddly
       together. */
    border-radius: var(--r);
  }
  /* The stage keeps its overflow -- that is what crops the cover-scaled film --
     and its corners, so the clip is cropped to the same shape as the frame
     around it. The hatch stays off: the film covers every pixel of the box, so
     that surface only ever showed as a border around the picture. */
  .nst-hero__stage {
    border-radius: var(--r);
    background: transparent;
  }
  .nst-hero__stage img,
  .nst-hero__video {
    border-radius: 0;
  }
  /* The film fades into the chrome above it, the way the phone hero does. The
     copy already sits on a gradient at the bottom; the top was a hard line
     under the nav, which is the last edge that made the hero read as a panel
     dropped on the page rather than the page itself.

     z-index 3: the media here is absolutely positioned at inset 0 with the copy
     over it, and a fade at 2 would tie with the film and lose on DOM order --
     which is exactly how this failed on mobile before it was raised. */
  .nst-hero__stage::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 150px;
    z-index: 3;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      rgba(8, 8, 10, .92) 0%,
      rgba(8, 8, 10, .74) 18%,
      rgba(8, 8, 10, .44) 46%,
      rgba(8, 8, 10, .16) 74%,
      rgba(8, 8, 10, 0) 100%);
  }
  /* No rule between the film and the queue beside it. */
  .nst-upnext {
    border-left: 0;
    border-top: 0;
    background: transparent;
  }
}

/* --- a hero that is a quote ------------------------------------------------- */

/* The In Their Words hero prints the athlete's caption where every other section
   prints a classifier headline, so it needs different type. The base headline is
   uppercase at up to 64px, which is right for "COMMITS TO WISCONSIN" and wrong
   for a sentence somebody said -- it turns a quiet line into a shout, and a long
   one runs out of room at three lines.

   Sentence case, smaller, and given a fourth line. The opening quote mark is what
   marks it as speech rather than a headline that forgot its capitals. */
.nst-hero__stage.is-quoted .nst-hero__title {
  text-transform: none;
  font-size: clamp(26px, 2.5vw, 40px);
  line-height: 1.08;
  letter-spacing: 0;
  -webkit-line-clamp: 4;
}
.nst-hero__stage.is-quoted .nst-hero__title::before {
  content: "\201C";
  /* Optical, not layout: the mark hangs left of the text rather than indenting
     the first line away from everything below it. */
  margin-left: -.5em;
  opacity: .6;
}
.nst-hero__stage.is-quoted .nst-hero__title::after {
  content: "\201D";
  opacity: .6;
}

/* The accent belongs to the athlete, not the filing.

   The hero kicker made the whole line orange and greyed only its last child, so
   "RECRUITING" arrived in the accent colour and the athlete's name in grey --
   the loudest thing on the byline was the section the model filed the post
   under. A reader scans that line for the person.

   So the emphasis swaps: the section label reads as the quiet label it is, and
   the name carries the orange. Stated after the rules above rather than editing
   them, because the same kicker class is used where that colouring is right. */
.nst-hero__kicker [data-hero-section] { color: var(--label); }
.nst-hero__kicker .nst-hero__who,
.nst-hero__kicker [data-hero-who] { color: var(--orange); }
/* The sport, when it is drawn at all, stays quieter than the name. */
.nst-hero__kicker .nst-hero__sport { color: var(--label); }

/* The network written out under the page.

   Everything above it is painted by JavaScript, so a crawler was served an
   empty shell with no way through to any of the athlete pages. This is the same
   content as plain HTML: every athlete linked to their own app.

   It is set quietly and kept below the fold -- a reader who gets this far has
   an index of the whole network, which is a reasonable thing to find at the
   foot of a site. Hiding it outright would be the wrong trade: content behind
   display:none is discounted by search engines, and rightly. */
.nst-index {
  border-top: 1px solid var(--line, rgba(255,255,255,.1));
  margin: 64px var(--gutter, 24px) 0;
  padding: 40px 0 64px;
  color: var(--dim, var(--label));
  font-size: 13px;
  line-height: 1.6;
}
.nst-index h2 {
  font-family: var(--cond, sans-serif);
  font-size: 13px; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--text, var(--text));
  margin: 0 0 10px;
}
.nst-index h2 + p { margin: 0 0 22px; max-width: 62ch; }
.nst-index ul {
  list-style: none; margin: 0 0 34px; padding: 0;
  /* Columns rather than one long run -- 156 names in a single list is a page of
     scrolling on its own. */
  columns: 240px 4; column-gap: 26px;
}
.nst-index li { break-inside: avoid; margin: 0 0 5px; }
.nst-index a { color: var(--text, var(--text)); text-decoration: none; }
.nst-index a:hover { color: var(--orange, #ff4a15); }
.nst-index span { color: var(--dim, var(--label)); }
@media (max-width: 760px) {
  .nst-index { margin-top: 40px; padding: 28px 0 44px; }
  .nst-index ul { columns: 160px 2; }
}

/* The page in daylight.

   Every colour above is a token, so a theme is a second set of values rather
   than a second stylesheet. The attribute sits on <html> -- the same hook
   mobile.html already uses -- so the whole page turns with one class and no
   rule below has to know which theme it is in.

   Not a straight inversion. Black text on white at the same weight reads
   heavier than white on black, so the greys are pulled further apart than
   their dark counterparts and the ink stops short of pure black. The orange
   is darkened a little: #ff4a15 vibrates against white, and on a label it was
   unreadable.

   The panes flip from lit-along-the-top to dropped-with-a-shadow, because on
   a white page a highlight along the top edge is invisible and only the
   shadow does any work. */
:root[data-theme="light"] {
  --ink: #ffffff;
  --panel: #f7f7f5;
  --card: #ffffff;
  --line: #e4e4e0;
  --line-strong: #d2d2cc;
  --text: #16161a;
  --muted: #55555e;
  --dim: #77777f;
  --faint: #9a9aa2;
  --orange: #e03c0c;

  --edge: #dcdcd6;
  --edge-strong: #c4c4bc;
  --label: #6b6b73;
  --label-dim: #8e8e96;
  --well: #f2f2ee;
  --well-raised: #ffffff;
  /* The move-up green was picked for a black page; on white it washes out to
     near-invisible. Darkened until it carries on paper. */
  --rise: #0f9d58;

  --pane: #ffffff;
  --pane-lead: #fbfbf9;
  --pane-edge: inset 0 0 0 1px rgba(16,16,20,.08),
               0 10px 30px rgba(16,16,20,.06);
  --line-glass: rgba(16,16,20,.12);
  --line-glass-strong: rgba(16,16,20,.20);
}

/* The daylight switch.

   Sits in the header beside LOG IN. One button showing the theme it will move
   to -- a sun on the dark page, a moon on the light one -- rather than two
   controls or a label that has to be read. */
.nst-theme {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; padding: 0;
  border-radius: 999px; cursor: pointer;
  background: transparent;
  border: 1px solid var(--edge);
  color: var(--muted);
  transition: color .18s ease, border-color .18s ease, background .18s ease;
}
.nst-theme:hover {
  color: var(--text); border-color: var(--edge-strong);
  background: var(--well);
}
/* Size only. `display` belongs to the rules below -- setting it here as
   `.nst-theme svg` outranked `.nst-theme__moon` on specificity, so both icons
   showed at once. */
.nst-theme svg { width: 17px; height: 17px; }
/* Show the one you are going to get. */
.nst-theme__sun { display: block; }
.nst-theme__moon { display: none; }
:root[data-theme="light"] .nst-theme__sun { display: none; }
:root[data-theme="light"] .nst-theme__moon { display: block; }

/* Copy that sits on the film stays light, whatever the page is.

   The hero prints its headline, byline, replies and figures directly over the
   clip. Those took --text like everything else, so in daylight they turned
   near-black over a dark frame and vanished -- the headline, the athlete's
   name and the app button all unreadable on the one thing the page leads with.

   The footage is its own background and does not change with the theme, so
   neither does the type on it. Stated once here rather than theming each rule,
   and scoped to the stage so the copy *below* the clip on a phone still takes
   the page's own colours. */
/* The watch player is film too.

   Its stage is dark in both themes -- the clip is the background -- but the
   overlay's type takes the page's ink, so in daylight the headline, the
   "3.6K PLAYS · POSTED 28D AGO" line and the athlete's name all went
   near-black on a dark frame. The hero and the preview's cinema each got this
   treatment already; this is the same surface under a different name, and it
   was missed because .nst-preview__cinema is the athlete preview's overlay,
   not the network page's.

   Restated as tokens on the layer rather than per rule, so anything added
   inside the stage later is light without a second fix. */
:root[data-theme="light"] .nst-wstage__over {
  color: #f4f4f2;
  --text: #f4f4f2;
  --muted: #d8d8d4;
  --dim: #b4b4b0;
  --label: #c8c8c4;
  --label-dim: #a8a8a4;
  --line-glass: rgba(255,255,255,.20);
  --line-glass-strong: rgba(255,255,255,.34);
  --edge: rgba(255,255,255,.24);
  --edge-strong: rgba(255,255,255,.40);
  --orange: #ff4a15;
}
/* The two that name a colour of their own rather than take the token. */
:root[data-theme="light"] .nst-wstage__meta { color: #d8d8d4; }
:root[data-theme="light"] .nst-wstage__title { color: #f4f4f2; }

:root[data-theme="light"] .nst-hero__stage {
  /* Set on the element as well as in the token: the headline names no colour
     of its own and inherits one from further up the page, so redefining the
     token alone left it inheriting the dark ink it had already taken. */
  color: #f4f4f2;
  --text: #f4f4f2;
  --muted: #d8d8d4;
  --dim: #b4b4b0;
  --label: #c8c8c4;
  --label-dim: #a8a8a4;
  --line-glass: rgba(255,255,255,.20);
  --line-glass-strong: rgba(255,255,255,.34);
  --edge: rgba(255,255,255,.24);
  --edge-strong: rgba(255,255,255,.40);
  /* The accent goes back to the bright one: it is over film, not paper. */
  --orange: #ff4a15;
}

/* Type printed on a picture stays light, whatever the page is.

   The hero was fixed for this already; every other card on the page has the
   same shape and was missed. A poster is its own background and does not turn
   with the theme, so in daylight the titles, names and counts sitting on one
   went near-black on a dark frame -- the rail, the moment cards, the sport
   tiles, the most-watched grid, the shorts and the reels all unreadable.

   Scoped to the blocks that actually overlay media, so the copy set on the
   page itself still takes the page's own ink. Same values the hero uses. */
:root[data-theme="light"] :is(
  .nst-card, .nst-reel, .nst-sportcard,
  .nst-short, .nst-mw__lead, .nst-mw__tile, .nst-watched__tile,
  .nst-programs__row, .nst-words__camera,
  /* Found by walking the rendered page for dark type overlapping a picture,
     rather than by guessing at the class names: these sit in wrappers the
     list above does not name. */
  .nst-mw__badge, .nst-mw__secondline,
  .nst-words__flag, .nst-watched__title,
  .nst-card .nst-kicker, .nst-reel .nst-kicker, .nst-mw__lead .nst-kicker,
  .nst-watchtile .nst-kicker, .nst-short .nst-kicker
) {
  color: #f4f4f2;
  --text: #f4f4f2;
  --muted: #d8d8d4;
  --dim: #b4b4b0;
  --label: #c8c8c4;
  --label-dim: #a8a8a4;
  --line-glass: rgba(255,255,255,.20);
  --line-glass-strong: rgba(255,255,255,.34);
  --edge: rgba(255,255,255,.24);
  --edge-strong: rgba(255,255,255,.40);
  --orange: #ff4a15;
}

/* The rail and the transport read on the page, not on the film.

   Both were swept into the rule above because each row carries a thumbnail --
   but the type sits *beside* that picture on the page's own background, not on
   it. Making them light left the titles, the names and the now-playing line
   washed out to near-nothing against white. Only the time badge is genuinely
   over the frame, so only that stays light. */
:root[data-theme="light"] :is(.nst-upnext__item, .nst-playing) { color: var(--text); }
:root[data-theme="light"] :is(.nst-upnext__title, .nst-playing__title) { color: var(--text); }
:root[data-theme="light"] :is(.nst-upnext__name, .nst-playing__who) { color: var(--muted); }
:root[data-theme="light"] .nst-upnext__plays { color: var(--orange); }
/* The rail's badge sits on the thumbnail, so it stays light. The transport's
   clock sits on the bar, so it takes the page's ink -- pinning both together
   left "0:07 / 3:47" white on white. */
:root[data-theme="light"] .nst-upnext__time { color: #f4f4f2; }
:root[data-theme="light"] .nst-playing__time { color: var(--muted); }

/* Copy that reads on the page takes the page's ink.

   Found by walking the rendered page for text whose colour sits close to the
   background behind it -- rather than one screenshot at a time, which is how
   the first few of these got missed. Every one is on the white page, not on a
   picture: the biggest-of-the-week card and its caption, the quotes in In
   their words, the Motion captions, the ranking figures, a bare avatar
   initial. They were caught by the over-the-film rule and washed out. */
:root[data-theme="light"] :is(
  .nst-biggest__title, .nst-biggest__who, .nst-mw__num, .nst-mw__who,
  .nst-programs__num, .nst-avatar__initial
) { color: var(--text); }
/* Except the podium, where the same name is printed on the clip rather than
   beside it. .nst-mw__who is one class doing two jobs: on the numbered rows it
   sits on the white page and wants the page's ink, but in the lead and the two
   second tiles it is over film. Taking the page's ink there put the athlete's
   name in near-black on a dark frame -- 02 and 03 had a name you could only
   read by knowing it was there. Scoped by ancestor, so the rows keep theirs. */
:root[data-theme="light"] :is(.nst-mw__lead, .nst-mw__second) .nst-mw__who {
  color: #d8d8d4;
}
/* Except on a phone, where the ranking figure moves onto the thumbnail rather
   than sitting beside it -- the moments sheet gives it a shadow and light ink
   there for exactly that reason, and this was overriding it, so 06, 07 and 09
   were dark numbers on dark pictures. */
@media (max-width: 760px) {
  :root[data-theme="light"] .nst-mw__num { color: #f4f4f2; }
}
:root[data-theme="light"] :is(
  .nst-biggest__caption, .nst-words__quiet, .nst-post__said, .nst-words__said
) { color: var(--muted); }

/* The transport, in daylight.

   It took --panel, which on the dark page is a surface lifted off the ink but
   in daylight is a grey laid over white -- the bar read as a dropped-out strip
   rather than part of the page. And its top edge resolved to the text ink, so
   a near-black rule ran the width of the screen under the hero.

   White, with a hairline above it and the page's own shadow doing the lifting. */
/* The transport, in daylight -- glass rather than a white slab.

   The dark build made this pane half-transparent over a deep blur so the film
   still shows through it; solid white here would have been the one surface on
   the page that hides what it sits on. Same idea, inverted: a light pane at 62%,
   the same blur, and the saturation lift that keeps colour coming through
   instead of washing to grey. */
:root[data-theme="light"] .nst-playing {
  background: rgba(255, 255, 255, .62);
  backdrop-filter: blur(26px) saturate(1.6);
  -webkit-backdrop-filter: blur(26px) saturate(1.6);
  border-top: 1px solid rgba(16, 16, 20, .10);
  box-shadow: 0 -12px 32px rgba(16, 16, 20, .10);
}
/* Controls on a light pane: an ink film rather than a white one, or they vanish
   into the glass they sit on. */
:root[data-theme="light"] .nst-playing__btn {
  background: rgba(16, 16, 20, .07);
  border: 1px solid rgba(16, 16, 20, .10);
}
:root[data-theme="light"] .nst-playing__btn--step {
  background: none;
  border-color: transparent;
}
/* No blur to hide behind, so the bar goes back to being opaque rather than
   letting the clip read through it at 62%. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  :root[data-theme="light"] .nst-playing { background: #ffffff; }
  .nst-playing { background: var(--panel); }
}

/* The band, in daylight.

   A full-width orange bar is the loudest thing on a white page -- it fought
   the hero for the eye before a reader had read a word. White reads as part
   of the page, with the news in the page's own ink and the diamonds keeping
   the accent so the strip still has the mark on it.

   A hairline under it, since without the block of colour there is nothing to
   separate it from the header below. */
:root[data-theme="light"] .nst-ticker {
  background: #ffffff;
  color: var(--text);
}
/* And black on the dark page, for the same reason it is white on the light
   one: a full-width band of orange is the loudest thing on the screen, and it
   was taking the eye before the film under it. The news reads in the page's
   own ink with the diamonds keeping the accent. */
:root:not([data-theme="light"]) .nst-ticker {
  background: var(--ink);
  color: var(--text);
  border-bottom: 1px solid var(--line);
}
:root:not([data-theme="light"]) .nst-ticker__run > span { color: var(--text); }
:root:not([data-theme="light"]) .nst-ticker__run > span:nth-child(even) { color: var(--orange); }
:root[data-theme="light"] .nst-ticker__run > span { color: var(--text); }
/* The diamonds between items keep the accent. */
:root[data-theme="light"] .nst-ticker__run > span:nth-child(even) { color: var(--orange); }

/* No rule above the rail's foot in daylight. On the dark page that hairline
   separates two surfaces; on white the rail has no surface of its own, so the
   line is drawing a box side that is not there. The space does the same work. */
:root[data-theme="light"] .nst-upnext__foot { border-top: 0; }

/* The tabs carry marks, and the row they sat on goes to the film.

   HOME / APPS / WATCH / TRAVEL was a 44px band across the page for four words.
   That height came off the clip on every screen. With a mark on each tab the
   row folds up into the header's own line -- the head has the width for it,
   sitting left of the Ask field -- and the hero starts 44px higher.

   An earlier pass stacked these into the left gutter instead. That gutter is
   32px, so the labels ran straight over the wordmark. The header row is where
   the space actually is. */
.nst-nav > :is(span, a) {
  display: inline-flex; align-items: center; gap: 8px;
  cursor: pointer;
}
.nst-nav > :is(span, a) svg { width: 17px; height: 17px; flex: 0 0 auto; }
.nst-nav > :is(span, a) b { font-weight: inherit; font-size: inherit; letter-spacing: inherit; }


/* The menu goes behind a button on desktop too.

   HOME / APPS / WATCH / TRAVEL sat as a 44px row across the page, which is
   height spent on four words on a page whose point is the film. The phone has
   answered this for a long time: a burger beside the mark, opening the same
   nav as a sheet. Desktop uses it now as well, so the row goes and the hero
   starts that much higher.

   No new machinery -- the switch, the label and the drawer already exist and
   are already wired. This only stops hiding them above 760px. */
@media (min-width: 761px) {
  .nst-nav__burger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; margin-right: 4px;
    flex: 0 0 auto; cursor: pointer; color: var(--text);
    border-radius: 999px;
    transition: background .16s ease;
  }
  .nst-nav__burger:hover { background: var(--well); }
  .nst-nav__bars { position: relative; width: 18px; height: 2px; background: currentColor; flex: 0 0 auto; }
  .nst-nav__bars::before, .nst-nav__bars::after {
    content: ""; position: absolute; left: 0; width: 18px; height: 2px; background: currentColor;
    transition: transform .18s ease, opacity .18s ease;
  }
  .nst-nav__bars::before { top: -6px; }
  .nst-nav__bars::after { top: 6px; }
  .nst-nav__switch:checked ~ .nst-head .nst-nav__bars { background: transparent; }
  .nst-nav__switch:checked ~ .nst-head .nst-nav__bars::before { transform: translateY(6px) rotate(45deg); }
  .nst-nav__switch:checked ~ .nst-head .nst-nav__bars::after { transform: translateY(-6px) rotate(-45deg); }

  /* The tabs are a sheet under the header rather than a row of the page, so
     they take no height until they are asked for. */
  .nst-nav { display: none; }
  .nst-nav__switch:checked ~ .nst-nav {
    display: flex; flex-direction: column; align-items: stretch;
    gap: 0; padding: 8px 0 14px;
    background: var(--panel);
    border-bottom: 1px solid var(--line);
  }
  .nst-nav__switch:checked ~ .nst-nav > :is(span, a) {
    padding: 13px var(--gutter); border-bottom: 0;
  }
  .nst-nav__switch:checked ~ .nst-nav > :is(span, a):hover { background: var(--well); }
  .nst-nav__switch:checked ~ .nst-nav .is-active { border-bottom: 0; padding-bottom: 13px; }
  /* The rail title names the column of clips beside the hero, not a tab, so it
     does not belong in a menu of sections. It rides on the header's own line,
     right-aligned over the rail it names, and stays there whether the sheet is
     open or shut. */

  .nst-head { padding-top: 16px; padding-bottom: 14px; }
}

/* The hero takes more of the window, and the transport sits lower.

   The frame was capped at 620px with 250px reserved above it for the chrome.
   Moving the menu behind a button gave some of that back, so the cap goes to
   760 and the reservation down to 210 -- the film fills the screen it is the
   point of, and the bar that describes it moves down with it rather than
   floating in the middle of the page. */
@media (min-width: 761px) {
  .nst-playing { padding-top: 14px; padding-bottom: 14px; }
}

/* The rail ends on a whole row.

   Its list is a two-across grid of 245px tiles in a column sized off the hero,
   so the last row was cut wherever the arithmetic landed -- 710px of list is
   2.9 rows, and the third was sliced through its titles, leaving "A DAY IN THE
   LIFE TRAINING" half-drawn above the scroll label.

   Rounding the list down to a multiple of the row means the fade at the bottom
   falls between tiles rather than through one. The rail still scrolls; it just
   does not advertise a broken row while it sits still. */
@media (min-width: 761px) {
  /* A row is never cut through its titles.

     The rail's scrolling box is sized off the hero, so its last visible row
     landed wherever the arithmetic fell -- at 712px of a 245px row that is
     2.9 rows, and the third was sliced through the tile captions, leaving "A
     DAY IN THE LIFE TRAINING" half-drawn under the fold.

     A fade over the last stretch instead of a hard edge: the cut still happens
     -- the list scrolls, so it must -- but it reads as more to come rather
     than as a row that failed to draw. The box is built in JS with no class of
     its own, so it is matched as the rail's child holding the tiles. */
  .nst-upnext > :has(> .nst-upnext__item) {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 46px), transparent);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 46px), transparent);
  }
}

/* A lighter wash on the film in daylight.

   The scrim is two gradients -- .82 in from the left, .78 up from the bottom
   -- sized to carry white copy over any frame on a black page, where a dark
   corner is just more of the page. On a white one it reads as a smudge laid
   over the picture, and the eye goes to it before the athlete.

   The copy still has to be legible on whatever the clip happens to show, so
   this thins the wash rather than dropping it, and pulls both gradients in so
   they cover the type and let go of the rest of the frame. */
:root[data-theme="light"] .nst-hero__scrim {
  background:
    linear-gradient(90deg, rgba(8,8,10,.58) 0%, rgba(8,8,10,.22) 26%, rgba(8,8,10,0) 46%),
    linear-gradient(0deg, rgba(8,8,10,.56) 0%, rgba(8,8,10,.12) 26%, rgba(8,8,10,0) 44%);
}

/* The drawer's own theme row. Only in the drawer -- above 760 the round switch
   beside LOG IN does this job, and two of them on one screen is one too many. */
.nst-nav__theme { display: none; }

/* The roster band takes the page, not a panel.

   It uses --panel to lift itself off the ink on the dark page, where a surface
   a shade above black reads as a section of its own. In daylight that same
   token is #f7f7f5, which against white is not a lift -- it is a grey slab
   behind the run of apps.

   The section is marked out by its heading and the space around it; it does
   not need a tone as well. */
:root[data-theme="light"] .nst-section--roster { background: transparent; }

/* A watch tile is a card, not a picture with words on it.

   Its thumbnail sits at the top and the caption underneath, on the tile's own
   surface -- so the whole tile was in the over-the-film list and its title went
   white on a white card. Only the time badge is genuinely on the picture. */
:root[data-theme="light"] .nst-watchtile { color: var(--text); }
:root[data-theme="light"] .nst-watchtile__title { color: var(--text); }
:root[data-theme="light"] :is(.nst-watchtile__who, .nst-watchtile__meta) { color: var(--muted); }
:root[data-theme="light"] .nst-watchtile__time { color: #fff; }

/* --- the menu's way in ------------------------------------------------------ */

/* The drawer's last row is the offer, for a reader who has scrolled past the
   header button and cannot see it any more.

   It inherits the nav's voice for its label -- Barlow Condensed, uppercase,
   tracked out -- so it sits with HOME / APPS / WATCH / TRAVEL rather than
   arriving as an advert. The sentence under it does not: prose at .16em tracking
   in caps is unreadable, so that line resets to the page's body voice. */
.nst-nav__start {
  align-items: flex-start;
  gap: 10px;
  text-decoration: none;
  color: var(--label);
}
.nst-nav__start svg {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  /* Down onto the label's baseline: the row is top-aligned for the two lines of
     copy, which would otherwise leave the icon floating above the text. */
  margin-top: 2px;
  color: var(--orange);
}
.nst-nav__startcopy {
  display: grid;
  gap: 3px;
  min-width: 0;
}
.nst-nav__start b {
  color: var(--text);
  font-weight: 600;
}
.nst-nav__startsub {
  font-family: 'Barlow', sans-serif;
  font-size: 13px;
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.3;
  color: var(--dim);
}
.nst-nav__start:hover b { color: var(--orange); }


/* Recruiting and its two figures on the same line.

   The heading is a flex item with nothing stopping it growing, so at 358px it
   took the whole row on its own -- "RECRUITING" is 24px of type sitting in a
   358px box -- and pushed "43 WITH OFFERS - 12 COMMITTED" onto a line of its
   own underneath. The count belongs beside the word it counts.

   The filters keep their own row: they are five controls, and squeezing them
   onto the title line would leave nothing legible on a phone. */
html body .nst-section__head--recruit {
  display: flex !important;
  flex-wrap: wrap !important;
  align-items: baseline !important;
  column-gap: 12px !important;
  row-gap: 8px !important;
}

html body .nst-section__head--recruit h2 {
  flex: 0 0 auto !important;
  margin: 0 !important;
}

/* Takes the rest of the title line and sits at its end. */
html body .nst-section__head--recruit [data-offer-reach] {
  flex: 1 1 auto !important;
  min-width: 0 !important;
  text-align: right !important;
  white-space: nowrap !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
}

/* Forced onto the next row, full width, so the five filters have the line to
   themselves. Five words do not fit across a phone whatever is done to them,
   so the row scrolls sideways rather than trimming "CAMP INVITES" against the
   edge -- and it is padded to the gutter at both ends so the first and last
   filter are whole when it is at either end of its travel. */
html body .nst-section__head--recruit [data-feed-filters] {
  flex: 1 0 100% !important;
  display: flex !important;
  flex-wrap: nowrap !important;
  overflow-x: auto !important;
  scrollbar-width: none !important;
  -webkit-overflow-scrolling: touch !important;
  gap: 18px !important;
}

html body .nst-section__head--recruit [data-feed-filters]::-webkit-scrollbar {
  display: none !important;
}

html body .nst-section__head--recruit [data-feed-filters] > * {
  flex: 0 0 auto !important;
  white-space: nowrap !important;
}
