/* The NIL Apps directory, as a view inside the home page.

   The standalone page at /nilsportstech-nil-apps owns the same design in
   nilsportstech-nil-apps.css. Here it has to coexist with the front page's
   stylesheet, which already defines .nst-grid, .nst-chip, .nst-btn--orange and
   .nst-foot differently -- loading that sheet whole would restyle the front
   page's card grids and roster chips. So every rule below is scoped to
   .nst-apps, and the tile grid is .nst-appgrid rather than .nst-grid.

   Tokens come from nilsportstech-network-v2.css, which loads first. */

.nst-apps[hidden] { display: none; }
/* The ticker runs on the front page's moments, so it goes with that view. Its
   own rule sets display:flex, which outranks the bare [hidden] attribute. */
.nst-ticker[hidden] { display: none; }

/* --- masthead ------------------------------------------------------------- */

/* One frame, layered. The athlete is the backdrop and the copy sits on top of
   it; the rail keeps its own column on the right. The previous version put the
   photo in a panel beside the copy, which left an L-shaped hole -- to the right
   of the headline and above the frame -- that no padding could close, because
   nothing was designed to occupy the middle of the block. */
.nst-apps .nst-masthead { position: relative; display: grid; grid-template-columns: 1fr 420px; grid-template-rows: auto 1fr; height: clamp(460px, 46vw, 620px); border-bottom: 1px solid var(--line); isolation: isolate; }

/* The image spans the copy column only -- the rail stays legible on flat black
   rather than fighting a photo behind eight rows of small type. */
.nst-apps .nst-spot { position: absolute; inset: 0 420px 0 0; z-index: 0; display: block; overflow: hidden; cursor: pointer; background: var(--hatch); border-radius: var(--r); }
.nst-apps .nst-spot[hidden] { display: none; }
.nst-apps .nst-spot__shot { position: absolute; inset: 0; }
.nst-apps .nst-spot__shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; transition: transform 1.2s cubic-bezier(.22,.61,.36,1); }
.nst-apps .nst-masthead:hover .nst-spot__shot img { transform: scale(1.04); }
/* Two washes, not one: a left-to-right ramp so the headline always has a dark
   bed under it whatever the photo, and a foot ramp for the name plate. Without
   the horizontal one, white type lands on a bright jersey and disappears. */
.nst-apps .nst-spot__shot::after { content: ""; position: absolute; inset: 0; background:
  linear-gradient(90deg, rgba(8,8,10,.95) 0%, rgba(8,8,10,.86) 26%, rgba(8,8,10,.42) 55%, rgba(8,8,10,.12) 100%),
  linear-gradient(rgba(8,8,10,.55) 0%, rgba(8,8,10,.15) 34%, rgba(8,8,10,.90) 100%); }

/* Copy rides over the image, pinned to the top of the frame. */
.nst-apps .nst-masthead__lede { position: relative; z-index: 1; grid-column: 1; padding: 44px var(--gutter) 0; pointer-events: none; align-self: start; }
.nst-apps .nst-masthead__title { margin: 0; font-family: var(--cond); font-size: clamp(48px, 5.7vw, 82px); font-weight: 700; text-transform: uppercase; line-height: .92; padding-top: 6px; text-shadow: 0 2px 30px rgba(0,0,0,.5); }
.nst-apps .nst-masthead__body { margin: 0; font-size: 18px; line-height: 1.55; color: var(--muted); padding-top: 14px; max-width: 520px; }

/* The name plate sits at the foot of the same frame, so the block reads as one
   image with its subject named rather than a photo with a caption beside it. */
.nst-apps .nst-spot__body { position: relative; z-index: 1; grid-column: 1; align-self: end; padding: 0 var(--gutter) 34px; min-width: 0; cursor: pointer; }
.nst-apps .nst-spot__body[hidden] { display: none; }
.nst-apps .nst-spot__label { font-family: var(--mono); font-size: 10px; letter-spacing: .18em; color: var(--orange); }
.nst-apps .nst-spot__name { font-family: var(--cond); font-size: clamp(38px, 3.4vw, 52px); font-weight: 700; text-transform: uppercase; line-height: .98; padding-top: 8px; text-wrap: balance; }
.nst-apps .nst-spot__body:hover .nst-spot__name { color: var(--orange); }
.nst-apps .nst-spot__line { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; color: var(--muted); text-transform: uppercase; padding-top: 6px; }
.nst-apps .nst-spot__tags { display: flex; gap: 6px; flex-wrap: wrap; padding-top: 10px; }

/* The rail owns the second column across both rows. */
.nst-apps .nst-moving { position: relative; z-index: 1; grid-column: 2; grid-row: 1 / -1; padding: 26px; border-left: 1px solid var(--line); background: var(--bg); display: flex; flex-direction: column; min-height: 0; }
.nst-apps .nst-moving__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding-bottom: 12px; flex: 0 0 auto; }
/* The head stays put and the list scrolls under it, so a long fortnight is
   reachable without stretching the masthead to fit every row. */
.nst-apps .nst-moving > [data-moving] { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #3a3a42 transparent; }
.nst-apps .nst-moving > [data-moving]::-webkit-scrollbar { width: 6px; }
.nst-apps .nst-moving > [data-moving]::-webkit-scrollbar-thumb { background: #3a3a42; border-radius: 3px; }
.nst-apps .nst-moving__window { font-family: var(--mono); font-size: 10px; letter-spacing: .16em; color: var(--dim); }
/* Eight rows rather than the comp's four, so each one is tighter and the tag
   column is fixed -- ragged tag widths made the names impossible to scan. */
.nst-apps .nst-moving__item { display: flex; align-items: baseline; gap: 12px; padding: 9px 0; border-top: 1px solid var(--line); font-size: 14px; line-height: 1.35; cursor: pointer; }
.nst-apps .nst-moving__item:last-child { border-bottom: 1px solid var(--line); }
.nst-apps .nst-moving__item:hover .nst-moving__text { color: var(--text); }
.nst-apps .nst-moving__tag { flex-shrink: 0; width: 52px; font-family: var(--mono); font-size: 9px; letter-spacing: .1em; color: var(--dim); }
/* The scarcer the event, the louder the tag. */
.nst-apps .nst-moving__tag--commit { color: var(--ink); background: var(--orange); text-align: center; padding: 3px 0; font-weight: 700; }
.nst-apps .nst-moving__tag--offer { color: var(--orange); }
.nst-apps .nst-moving__tag--film { color: #8b8b93; }
.nst-apps .nst-moving__text { color: var(--muted); min-width: 0; }
.nst-apps .nst-moving__text strong { color: var(--text); font-weight: 600; }

/* --- filters -------------------------------------------------------------- */

/* One row at every width, scrolling sideways when the pills outrun it.

   This carried flex-wrap: wrap, and because it is unmediaed in a sheet that
   loads after the phone sheet it beat the phone's own nowrap -- equal
   specificity, later wins, media query or not. That is how the strip kept
   folding into two rows however the mobile rules were written. */
.nst-apps .nst-filters {
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  overflow-x: auto; scrollbar-width: none;
  padding: 18px var(--gutter); border-bottom: 1px solid var(--line);
  font-family: var(--cond); font-size: 14px; letter-spacing: .14em; text-transform: uppercase;
}
.nst-apps .nst-filters::-webkit-scrollbar { display: none; }
.nst-apps .nst-filters__set { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; flex: 0 0 auto; }
.nst-apps .nst-filters__split { width: 1px; height: 26px; background: var(--line-strong); margin: 0 8px; }
/* The front page's .nst-chip is a static label; these are buttons.

   Fully round rather than the 6px --r-sm they carried. At that radius, in a row
   of eight, they read as a toolbar of boxes -- the oldest-looking thing left on
   the page once the cards were redrawn. The pill is also what the shape is
   called, and it now looks like one. */
.nst-apps .nst-chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 8px 10px 16px; border: 1px solid var(--line-strong);
  color: var(--label); background: none;
  font: inherit; letter-spacing: inherit; text-transform: inherit;
  cursor: pointer; border-radius: 999px;
  transition: border-color .18s ease, color .18s ease, background-color .18s ease;
}
/* The count rides in its own token, so the number reads as data attached to the
   filter rather than as part of its name. */
.nst-apps .nst-chip__count {
  font-family: var(--mono); font-size: 10px; letter-spacing: .08em;
  color: var(--dim); background: rgba(244,244,242,.07);
  padding: 3px 8px; border-radius: 999px; line-height: 1;
}
.nst-apps .nst-chip:hover { border-color: var(--edge-strong); color: var(--text); background: rgba(244,244,242,.04); }
.nst-apps .nst-chip:hover .nst-chip__count { color: var(--muted); }
/* Selected is the orange the rest of the page uses for "this is the live one",
   rather than the white block it used to invert to -- that read as a disabled
   control on a dark page and, in light mode, as a hole. */
.nst-apps .nst-chip.is-on {
  background: var(--orange); color: var(--ink); font-weight: 600; border-color: var(--orange);
}
.nst-apps .nst-chip.is-on .nst-chip__count { background: rgba(8,8,10,.22); color: rgba(8,8,10,.8); }
/* The idle count's wash is white ink at 7%, which is a token on a dark page and
   nothing at all on a white one. Light mode inverts it to dark ink. */
:root[data-theme="light"] .nst-apps .nst-chip__count { background: rgba(8,8,10,.06); }
:root[data-theme="light"] .nst-apps .nst-chip.is-on .nst-chip__count { background: rgba(8,8,10,.18); color: rgba(255,255,255,.92); }
/* A chip with no count keeps its right padding. */
.nst-apps .nst-chip:not(:has(.nst-chip__count)) { padding-right: 16px; }

.nst-apps .nst-sort { margin-left: auto; display: flex; align-items: center; gap: 10px; font-family: var(--mono); font-size: 11px; letter-spacing: .14em; color: var(--dim); text-transform: none; }
/* Matches the pills: round, no chrome dropdown well, with the caret drawn in. */
.nst-apps .nst-sort select {
  font: inherit; letter-spacing: inherit; color: var(--muted);
  background: none; border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 9px 34px 9px 16px; cursor: pointer;
  -webkit-appearance: none; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 18px) 52%, calc(100% - 13px) 52%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  transition: border-color .18s ease, color .18s ease;
}
.nst-apps .nst-sort select:hover { border-color: var(--edge-strong); color: var(--text); }

/* --- the grid ------------------------------------------------------------- */

/* Aligned to the gutter the filters and the header already sit in. It ran
   full-bleed at 2px, so with the masthead gone the first card was sheared off
   at the left edge while the pill above it started an inch inside -- the two
   rows read as belonging to different pages. The gap opens with it: at 2px the
   cards were a sheet rather than a set of cards. */
.nst-apps .nst-appgrid {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 14px; padding: 18px var(--gutter) 2px;
}
.nst-apps .nst-app { position: relative; height: 460px; background: var(--hatch); overflow: hidden; border-radius: var(--r); cursor: pointer; }
/* Stands in when an app has set no picture. Underneath the image rather than
   instead of it, so a slow or broken source uncovers a letter rather than an
   empty frame. The letter is set large and low-contrast: it should read as a
   surface, not as a headline competing with the name over it. */
.nst-apps .nst-app__mark {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: linear-gradient(150deg, #17171c, #101014);
  font-family: var(--cond); font-weight: 700; line-height: 1;
  font-size: clamp(56px, 32%, 150px); color: rgba(244,244,242,.09);
  user-select: none;
}
/* On desktop the frame is the card: the body still draws over it, so the frame
   fills the box and the picture positions against that. On a phone this is
   overridden and the frame becomes just the square, with the caption below. */
.nst-apps .nst-app__frame { position: absolute; inset: 0; overflow: hidden; border-radius: var(--r); }
.nst-apps .nst-app img { position: absolute; inset: 0; transition: transform .6s cubic-bezier(.22,.61,.36,1); }
.nst-apps .nst-app:hover img { transform: scale(1.04); }
.nst-apps .nst-app--lit { border-top: 2px solid var(--orange); }
/* The hero's scrim, at card scale.

   This used to be one bottom-up wash that stepped to var(--ink) at 88% -- fully
   opaque black across the lower half of every card. It was written to guarantee
   a surface for the headline, and it did, by painting out the picture behind
   it: the cards for apps whose poster is a portrait read as flat black
   rectangles with a name on them.

   The hero solves the same problem one section up and does not do this. It
   crosses two gradients, both landing transparent well before the far edge:
   ink pools where the type sits and the picture stays open away from it. Same
   idea here, sized for a tile rather than a stage -- the vertical carries the
   headline, the horizontal deepens the corner the type starts in, and neither
   reaches opaque. */
.nst-apps .nst-app__scrim { position: absolute; inset: 0; background:
  linear-gradient(90deg, rgba(8,8,10,.72) 0%, rgba(8,8,10,.28) 42%, rgba(8,8,10,0) 74%),
  linear-gradient(0deg, rgba(8,8,10,.88) 0%, rgba(8,8,10,.5) 26%, rgba(8,8,10,.12) 48%, rgba(8,8,10,0) 68%); }
.nst-apps .nst-app__flag { position: absolute; top: 14px; left: 14px; z-index: 2; display: flex; align-items: center; gap: 7px; font-family: var(--mono); font-size: 10px; letter-spacing: .16em; color: var(--muted); background: rgba(8,8,10,.72); backdrop-filter: blur(3px); padding: 5px 9px; border-radius: var(--r-sm); }
.nst-apps .nst-app__flag--live { color: var(--orange); }
.nst-apps .nst-app__flag--new { color: var(--ink); background: var(--orange); backdrop-filter: none; font-weight: 700; }
.nst-apps .nst-app__dot { width: 6px; height: 6px; background: var(--orange); border-radius: 50%; animation: nst-pulse 2s ease-in-out infinite; }
.nst-apps .nst-app__index { position: absolute; top: 14px; right: 14px; z-index: 2; font-family: var(--mono); font-size: 11px; color: var(--muted); background: rgba(8,8,10,.72); backdrop-filter: blur(3px); padding: 5px 8px; border-radius: var(--r-sm); }
/* Laid out like .nst-hero__body: a column with real gaps rather than each row
   pushing the next down with its own padding. */
.nst-apps .nst-app__body {
  position: absolute; left: 20px; right: 20px; bottom: 20px; z-index: 2;
  display: flex; flex-direction: column; gap: 10px;
}
/* The hero's kicker: orange mono, wide tracking, with the rule beside it. What
   the card had instead was the sport set in grey under the name, which said the
   same thing in the page's quietest voice. */
.nst-apps .nst-app__kicker {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--mono); font-size: 9px; letter-spacing: .2em;
  color: var(--orange); text-transform: uppercase;
  white-space: nowrap; overflow: hidden;
}
.nst-apps .nst-app__kicker .nst-rule { width: 100%; min-width: 12px; }
/* Avatar beside the name, the way the hero's byline reads. A card whose poster
   is a flyer or a bus used to show nothing of the athlete at all. */
.nst-apps .nst-app__who { display: flex; align-items: center; gap: 10px; min-width: 0; }
.nst-apps .nst-app__who .nst-avatar {
  width: 34px; height: 34px; font-size: 15px; flex-shrink: 0;
  border-radius: 50%; border-color: rgba(244,244,242,.28);
}
/* Card scale, not hero scale. A flat 38px was the stage's type on a 358px tile,
   which is why a long handle swallowed the frame. No text-shadow: that was the
   flat wash's crutch, and the ink now lives in the gradient. */
.nst-apps .nst-app__name {
  font-family: var(--cond); font-size: clamp(20px, 1.6vw, 26px); font-weight: 700;
  text-transform: uppercase; line-height: .98; min-width: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}
.nst-apps .nst-app:hover .nst-app__name { color: var(--orange); }
.nst-apps .nst-app__line { font-family: var(--mono); font-size: 10px; letter-spacing: .14em; color: #8b8b93; text-transform: uppercase; }
/* No padding-top: the body is a flex column with its own gap now, and the two
   stacked into a 20px step under the name. */
.nst-apps .nst-app__tags { display: flex; gap: 6px; flex-wrap: wrap; }
/* background:none matters -- the front page's .nst-tag loads first and fills
   solid orange, so an orange-on-orange tag read as an unreadable blob. */
.nst-apps .nst-tag { font-family: var(--mono); font-size: 10px; font-weight: 400; letter-spacing: .1em; color: var(--muted); background: none; border: 1px solid #2f2f36; border-radius: var(--r-sm); padding: 4px 8px; }
.nst-apps .nst-tag--offers { color: var(--orange); background: none; border-color: #ff4a1555; }
/* Plays carry the accent while the reader is sorting on them -- it is the
   column they asked to see, so it leads the tag row. */
.nst-apps .nst-tag--plays { color: var(--orange); background: none; border-color: #ff4a1555; }

.nst-apps .nst-more { display: flex; flex-direction: column; align-items: center; padding: 0 var(--gutter) 40px; }
/* The tail the observer watches. It needs a real box -- a zero-height element
   is never "intersecting", so the grid would stop loading at page one. */
.nst-apps .nst-more__tail { width: 100%; height: 1px; }
.nst-apps .nst-more__tail[hidden], .nst-apps .nst-more__none[hidden] { display: none; }
.nst-apps .nst-more__none { font-family: var(--mono); font-size: 12px; letter-spacing: .14em; color: var(--dim); }

/* --- close ---------------------------------------------------------------- */

.nst-apps .nst-cta { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 40px; padding: 60px var(--gutter); background: var(--panel); border-top: 1px solid var(--line); }
.nst-apps .nst-cta__title { font-family: var(--cond); font-size: clamp(38px, 4.2vw, 60px); font-weight: 700; text-transform: uppercase; line-height: .98; }
/* The front page's orange button is a hero action at 15px; the directory's is
   the page's closing call and sits larger. */
.nst-apps .nst-cta .nst-btn--orange { font-size: 18px; padding: 22px 40px; }

/* The nav reads as tabs once it switches views rather than only marking place. */
.nst-nav [data-view] { cursor: pointer; }
.nst-nav [data-view]:hover { color: var(--text); }

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

@media (max-width: 1180px) {
  .nst-apps .nst-appgrid { grid-template-columns: repeat(3, 1fr); }
  .nst-apps .nst-masthead {
    grid-template-columns: 1fr;
    /* And no fixed height with it. The desktop rule sets clamp(460px, 46vw,
       620px), which on a phone is 460 -- while the rows stacked into one
       column come to 640. The extra 180 spilled out of the section and the
       athlete's name and moment count printed straight through the filter
       chips below.

       It has to be undone here rather than in the phone sheet: that sheet
       loads before this one, so a height set here outlives it. */
    height: auto;
  }
  .nst-apps .nst-masthead__lede { border-right: 0; border-bottom: 1px solid var(--line); }
  /* Everything into the one column the masthead now has.

     What moved kept the desktop placement -- column 2 of a grid that has one
     -- so the browser invented a second column for it and split the row in
     half. The rail took the right half, the copy the left, and the athlete's
     photograph was squeezed to ten pixels down the edge of the screen.

     Stacked in order instead: the picture with the name on it, the lede, then
     the rail under both. */
  .nst-apps .nst-moving { grid-column: 1; grid-row: auto; border-left: 0; border-top: 1px solid var(--line); }
  /* And its full width back. The desktop rule pins it to "inset: 0 420px 0 0",
     which reserves 420px for the rail beside it -- on a 430px screen that
     leaves the photograph ten pixels wide down the edge. */
  .nst-apps .nst-spot { grid-column: 1; position: relative; inset: auto; width: 100%; }
  .nst-apps .nst-masthead__lede { grid-column: 1; }
  .nst-apps .nst-spot__body { grid-column: 1; }
}

@media (max-width: 900px) {
  .nst-apps .nst-appgrid { grid-template-columns: repeat(2, 1fr); }
  .nst-apps .nst-app { height: 380px; }
  .nst-apps .nst-app__name { font-size: 30px; }
  .nst-apps .nst-cta { grid-template-columns: 1fr; }
  .nst-apps .nst-sort { margin-left: 0; }
}

@media (max-width: 560px) {
  /* Three across, not one. A single column of 420px cards showed one athlete
     per screen and made the directory a scroll rather than something you look
     over -- the whole point of it is seeing who is on the network. */
  .nst-apps .nst-appgrid { grid-template-columns: repeat(3, 1fr); gap: 10px; padding: 10px var(--gutter) 4px; }
  /* The name comes off the picture.

     At a third of a phone's width there is no room to set type over a
     photograph and have both survive: the wash needed to carry the name ate
     the face, and the name still landed on whatever the picture was doing --
     "CHARLES WOODSON JR." over a helmet, "VICTOR BRADDOM" over a school bus.
     So the card stops being a frame with type on it and becomes a picture with
     a caption under it, which is what the roster section does at this width
     for the same reason.

     The card is no longer a fixed box either: it is the square plus whatever
     the caption needs. */
  .nst-apps .nst-app {
    height: auto; aspect-ratio: auto; background: none; overflow: visible;
    display: flex; flex-direction: column; gap: 7px;
  }
  /* The picture becomes its own frame -- the img is absolutely positioned, so
     it needs a box with a size to fill. */
  .nst-apps .nst-app > img,
  .nst-apps .nst-app > .nst-app__scrim,
  .nst-apps .nst-app > .nst-app__mark { border-radius: 12px; }
  .nst-apps .nst-app__frame {
    position: relative; aspect-ratio: 3 / 4; overflow: hidden;
    border-radius: 12px; background: var(--hatch); flex: 0 0 auto;
  }
  /* No wash: nothing sits on the picture now. */
  .nst-apps .nst-app__scrim { display: none; }
  .nst-apps .nst-app__body {
    position: static; inset: auto; left: auto; right: auto; bottom: auto;
    padding: 0; gap: 4px;
  }
  /* Type sized for the tile it is on rather than for a card a third of the
     screen wide. */
  .nst-apps .nst-app__name { font-size: 13px; line-height: 1.12; }
  .nst-apps .nst-app__meta,
  .nst-apps .nst-app__moments { font-size: 9px; letter-spacing: .04em; }
  /* The kicker and the avatar are furniture for a card with room. Under a
     125px picture they crowd the one thing the caption is for. */
  .nst-apps .nst-app__kicker,
  .nst-apps .nst-app__who .nst-avatar,
  .nst-apps .nst-app__line,
  .nst-apps .nst-app__tags { display: none; }
  .nst-apps .nst-app__index { top: 5px; right: 5px; font-size: 8px; padding: 2px 5px; }
  /* The first card's ribbon, cut to what a third of the row can hold. At full
     width it ran off the card and over the one beside it. */
  .nst-apps .nst-app__first {
    font-size: 7px; letter-spacing: .04em; padding: 3px 5px;
    max-width: calc(100% - 34px); overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap;
  }
  /* Two lines and then an ellipsis. "THE NCAA MENS CHAMPIONSHIP..." set three
     deep and pushed the moments pill off its own card. */
  .nst-apps .nst-app__name {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* The one-row filter strip is set in nilsportstech-network-v2-mobile.css,
     where the phone's filter rules already lived -- a second copy here would
     be two places to keep in step. Only the count token is sized here, since
     that element is this file's. */
  .nst-apps .nst-chip__count { font-size: 9px; padding: 3px 7px; }
}



/* The directory's lead sits on a photograph, so its copy stays light.

   Every line of it -- EVERY APP ON THE NETWORK, the strapline, the featured
   athlete's name and their sport -- prints over the picture, and all of it
   inherited the page's ink. In daylight that is near-black on a dark frame:
   the headline and the athlete's name were there but could not be read.

   The picture is its own background and does not turn with the theme, so
   neither does the type on it. Same treatment the main hero already has. */
/* The masthead prints on the same picture and is a sibling of the spot, not a
   child, so it needs naming too -- the headline and the athlete's name both
   state their colour rather than inheriting one. */
:root[data-theme="light"] .nst-apps :is(.nst-masthead__title, .nst-spot__name) {
  color: #f4f4f2;
}
:root[data-theme="light"] .nst-apps .nst-masthead__body { color: #d8d8d4; }
/* Only the two blocks that sit on the picture. Naming .nst-apps as the scope
   swept in the WHAT MOVED list beside it, which is on the white page -- every
   name in it went light on white. */
:root[data-theme="light"] .nst-apps :is(.nst-spot, .nst-masthead) {
  color: #f4f4f2;
  --text: #f4f4f2;
  --muted: #d8d8d4;
  --dim: #b4b4b0;
  --label: #c8c8c4;
  --line-glass: rgba(255,255,255,.20);
  --edge: rgba(255,255,255,.24);
  --orange: #ff4a15;
}

/* What moved is not on the picture.

   It sits inside the masthead block, so the rule that keeps the lead's copy
   light over its photograph reached the list beside it as well -- every name
   in it went #d8d8d4 on a white page. The list has the page's own surface
   behind it, so it takes the page's ink. */
:root[data-theme="light"] .nst-apps .nst-moving {
  /* The tokens back, not the colour. The masthead redefines --text and --muted
     for its copy over the picture, and the list inherits them -- so asking for
     var(--text) here returned the light one and the names stayed white on
     white. Restated, everything under it resolves against the page again. */
  --text: #16161a;
  --muted: #55555e;
  --dim: #77777f;
  --label: #6b6b73;
  color: #16161a;
}

/* An app card is dark whatever the page is, so its copy stays light.

   The card's surface is --hatch, a striped placeholder the athlete's picture
   covers -- and where a picture is missing or still loading, that texture is
   what shows. It is dark in both themes and the light palette never restates
   it, so in daylight the card stayed black while the name on it followed the
   page down to near-black: AP3, JOEL DOE and BRYCEHUNTER were on their cards
   and could not be read.

   The whole card is a picture surface, so it takes the light ink the way the
   hero's copy does. */
:root[data-theme="light"] .nst-apps .nst-app {
  color: #f4f4f2;
  --text: #f4f4f2;
  --muted: #d8d8d4;
  --dim: #b4b4b0;
  --label: #c8c8c4;
  --line-glass: rgba(255,255,255,.20);
  --edge: rgba(255,255,255,.24);
  --orange: #ff4a15;
}
@media (min-width: 561px) {
  :root[data-theme="light"] .nst-apps :is(.nst-app__name, .nst-app__line, .nst-app__tags) {
    color: #f4f4f2;
  }
  :root[data-theme="light"] .nst-apps .nst-app__line { color: #c8c8c4; }
}
/* Below 560px only the frame is a picture -- the caption is under it, on the
   page. So the dark palette above belongs to the frame, and the card hands the
   page's own back to everything outside it.

   Restating the tokens rather than naming each element: the block above sets
   --text and four others on the card, so a rule that only fixed .nst-app__name
   would leave the sport line, the tags and anything added later still reading
   from the dark palette. This is the one place the two surfaces divide. */
@media (max-width: 560px) {
  :root[data-theme="light"] .nst-apps .nst-app__body {
    color: var(--ink);
    --text: #16161a;
    --muted: #55555e;
    --dim: #77777f;
    --label: #6b6b73;
  }
  :root[data-theme="light"] .nst-apps .nst-app__name { color: #16161a; }
  :root[data-theme="light"] .nst-apps .nst-app__line { color: #6b6b73; }
}
