/* PT-CODEGEN-01 §17. Streaming surfaces.
   Colours are brand tokens (--ptv-*) wherever the element sits on the PAGE, which is dark.
   Literals remain only inside components that carry their OWN light surface — cards, pills, the
   entitlement gate — where dark ink is the correct and readable choice.

   That distinction is the whole point: this file originally set a light-page text colour on the
   page root, so every streaming surface rendered near-black text on the near-black shell at about
   1.1:1. It looked like the sign-in button did nothing, because the page it advanced to was
   invisible. When adding a rule here, decide first what it sits ON. */

.ptv-catalogue, .ptv-watch, .ptv-purchase, .ptv-library, .ptv-signin {
    font-family: var(--ptv-font-body), system-ui, sans-serif;
    /* The shell is dark (--ptv-bg). This was a light-page literal and rendered
       near-black on near-black — every streaming page was unreadable. */
    color: var(--ptv-text);
    /* One content width across the product: 1200px, matching .ptv-main. */
    max-width: 1200px;
    margin: 0 auto;
    padding: 1.5rem 1rem 4rem;
}

.ptv-catalogue h1, .ptv-watch h1, .ptv-purchase h1, .ptv-library h1, .ptv-signin h1,
.ptv-row__title, .ptv-gate__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif; }
.ptv-purchase__price, .ptv-card__price, .ptv-gate__price {
    font-family: var(--ptv-font-mono), ui-monospace, monospace;
}

.ptv-row { margin-block: 2.5rem; }
.ptv-row__items { list-style: none; padding: 0; margin: 0; display: grid; gap: 1rem;
                  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
/* Every video row is a wrapping grid — home, channel pages, "More like this" — the same as the
   On demand library: every title is in view at once, none hidden past the edge of a carousel.
   (The prototype's horizontal shelves were replaced on the owner's direction, 2026-10-05.) */
.ptv-row__empty { color: var(--ptv-muted); }

/* The ranked shelf — "Top on the island this week". The outline numeral sits proud of the tile's
   left edge, per the prototype; the rank is decoration over an <ol>, so the order is still real
   list semantics for a screen reader. */
.ptv-top { margin-block: 0 1rem; }
/* A grid like every other video row; each cell is the numeral plus a tile that fills the rest. */
.ptv-top__items { list-style: none; display: grid; gap: 1rem 1.25rem;
                  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
                  padding: .625rem .125rem 1.125rem; margin: 0; }
.ptv-top__item { min-width: 0; }
.ptv-top__link { display: flex; align-items: flex-end; color: inherit; text-decoration: none;
                 transition: transform 0.2s ease; }
.ptv-top__link:hover { transform: scale(1.03); }
.ptv-top__link:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: 3px; }
.ptv-top__rank { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
                 font-size: 9.375rem; font-weight: 700; line-height: .72; letter-spacing: -.06em;
                 color: transparent; -webkit-text-stroke: 2px var(--tv-rank-stroke);
                 flex: none; margin-right: -1.625rem; position: relative; z-index: 1; }
.ptv-top__tile { position: relative; display: block; flex: 1; min-width: 0; aspect-ratio: 16 / 10;
                 border-radius: 12px; overflow: hidden; background: var(--ptv-surface-2);
                 box-shadow: 0 2px 12px var(--tv-tile-shadow); }
.ptv-top__art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
                opacity: .9; display: block; }
.ptv-top__scrim { position: absolute; inset: 0;
                  background: linear-gradient(to top, var(--tv-tile-scrim) 0%, var(--tv-tile-scrim-fade) 60%); }
.ptv-top__live { position: absolute; top: .5625rem; left: .5625rem; }
.ptv-top__label { position: absolute; left: .75rem; right: .625rem; bottom: .625rem;
                  font-size: .8125rem; font-weight: 700; line-height: 1.3; text-align: left;
                  color: var(--ptv-text); }

/* Dark tile, per the prototype. The card is no longer a white panel: the image IS the card, and
   the title sits beneath it on the page. A light panel here was the single biggest departure from
   the prototype and made the grid read as a shop listing rather than a video shelf. */
.ptv-card { display: block; text-decoration: none; color: inherit; transition: transform 0.2s ease; }
.ptv-card:hover { transform: scale(1.03); }
.ptv-card:hover .ptv-card__media { box-shadow: 0 10px 34px var(--tv-card-glow); }
.ptv-card:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: 3px; }
.ptv-card__media { position: relative; aspect-ratio: 16/9; border-radius: 12px; overflow: hidden;
    background: var(--ptv-surface-2); box-shadow: 0 2px 12px var(--tv-shade-40); }
.ptv-card__media img { width: 100%; height: 100%; object-fit: cover; opacity: .9; }
/* Artwork is optional and, on dev, usually absent. The prototype assumes every tile has an image,
   so a placeholder inheriting near-background tones made whole rows look empty — the cards were
   there, correctly sized and linked, and simply could not be seen. This has to read as a tile in
   its own right, which means a surface lighter than the page and an edge to bound it. */
.ptv-card__placeholder, .ptv-top__art--ph { width: 100%; height: 100%;
    background: var(--tv-art-placeholder);
    box-shadow: inset 0 0 0 1px var(--tv-hairline); }

/* The badges sit on the artwork, so this is what guarantees they are readable over a pale frame.
   Without it a bright thumbnail renders a white "Free" chip on near-white. */
.ptv-card__scrim { position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(to top, var(--tv-card-scrim) 0%, transparent 55%); }
.ptv-card__live { position: absolute; top: 10px; left: 10px; }

/* The region flag, top-right of the tile, opposite the LIVE chip. Same plum chrome as the price
   pill so it reads as part of the tile rather than a sticker on it. */
.ptv-card__flag { position: absolute; top: 10px; right: 10px; display: inline-flex;
    padding: 3px; background: var(--tv-pill-chrome); border: 1px solid var(--tv-hairline);
    border-radius: 6px; }
.ptv-card__flag .ptv-flag { border-radius: 2px; }

/* The prototype's tile pill: mono, one plum chrome for every state, the state in the words and
   the text colour — teal Free, gold Subscribers, white price. */
.ptv-card__pill { position: absolute; bottom: 10px; left: 10px;
    font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 12px; font-weight: 700;
    background: var(--tv-pill-chrome); border: 1px solid var(--tv-hairline);
    padding: 4px 10px; border-radius: 999px; color: #fff; }
.ptv-card__pill--free { color: var(--tp-accent-teal); }
.ptv-card__pill--subscription { color: var(--tp-accent-gold); }

/* ─── Views pill (plan §4.5; ported from prototypes/explore/explore.css) ─────────────────────
   Bottom-right of the tile, opposite the access pill. The same plum chrome, mono face and size
   as .ptv-card__pill so the two read as one pair of tile furniture, but in the dimmer text tone:
   the price is the call to action, the views are context. */
.ptv-card__views { position: absolute; bottom: 10px; right: 10px;
    display: inline-flex; align-items: center; gap: 5px;
    font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 12px; font-weight: 700; line-height: 1.3;
    background: var(--tv-pill-chrome); border: 1px solid var(--tv-hairline);
    padding: 4px 9px; border-radius: 999px; color: var(--tv-text-dim); }
.ptv-card__views svg { width: 12px; height: 12px; flex: none; }
/* The ranked shelf's tiles carry the label bottom-left, so the count sits top-right there. */
.ptv-top__views { position: absolute; top: .5625rem; right: .5625rem; bottom: auto; font-size: 11px; padding: 3px 8px; }
/* Episode rows and the category hero: the count as plain meta text, no chrome. */
.ptv-meta-views { display: inline-flex; align-items: center; gap: 4px; color: var(--tv-text-faint);
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 12px; font-weight: 700; }
.ptv-meta-views svg { width: 12px; height: 12px; }

.ptv-card__body { padding: 10px 2px 0; }
.ptv-card__title { font-size: 14px; font-weight: 700; line-height: 1.35; margin: 0;
    color: var(--ptv-text); text-wrap: pretty; }
.ptv-card__meta { color: var(--tv-text-faint); font-size: 12px; margin: 3px 0 0; }

/* Status pills always carry text; colour reinforces, never signals alone (WCAG 1.4.1). */
.ptv-pill { display: inline-block; padding: .125rem .5rem; border-radius: 999px; font-size: .75rem; font-weight: 600; }
.ptv-pill--free { background: rgba(0, 201, 167, .16); color: var(--tp-accent-teal, #00C9A7); }
.ptv-pill--ppv { background: rgba(106, 0, 255, .28); color: #C9A8FF;
                 /* Money is always mono. */
                 font-family: var(--ptv-font-mono), ui-monospace, monospace; }
.ptv-pill--subscription { background: rgba(255, 209, 102, .18); color: var(--tp-accent-gold, #FFD166); }.ptv-pill--active { background: #e6f7f3; color: #00564a; }
.ptv-pill--expired { background: var(--tp-grey-bg); color: #45454d; }

/* The hero's price pill: mono on the slightly lighter plum chrome, per the prototype. */
.ptv-hero__pill { font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 13px; font-weight: 700;
    background: var(--tv-hero-pill-chrome); border: 1px solid var(--tv-hairline);
    padding: 5px 12px; border-radius: 999px; color: #fff; }

.ptv-live { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; font-weight: 700;
            letter-spacing: 0.05em;
            background: rgba(0,0,0,.72); color: var(--tp-white); padding: 5px 12px; border-radius: 999px; }
/* The prototype draws the chip smaller as the tile shrinks: 11px on shelf cards, 10px on the
   ranked tiles, each with its own dot. */
.ptv-card__live .ptv-live { font-size: 11px; gap: 6px; padding: 4px 10px; }
.ptv-card__live .ptv-live__dot { width: 6px; height: 6px; }
.ptv-top__live .ptv-live { font-size: 10px; gap: 5px; padding: 3px 9px; }
.ptv-top__live .ptv-live__dot { width: 5px; height: 5px; }
/* On air is Carnival Orange, never red — the chip carries the colour, the dot stays legible on it. */
.ptv-live--on { background: var(--ptv-accent); }
.ptv-live__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ptv-accent); }
.ptv-live--on .ptv-live__dot { background: #fff; animation: ptv-pulse 2s ease-in-out infinite; }
.ptv-live--off .ptv-live__dot { background: #8a8a94; }
@keyframes ptv-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }

.ptv-health { display: inline-flex; align-items: center; gap: .375rem; font-size: .75rem; font-weight: 600;
              padding: .125rem .5rem; border-radius: 999px; background: var(--tp-grey-bg); color: #45454d; }
.ptv-health__dot { width: .5rem; height: .5rem; border-radius: 50%; background: currentColor; }
.ptv-health--healthy { background: #e6f7f3; color: #00564a; }
.ptv-health--degraded { background: #fff4e5; color: #6b3b00; }
.ptv-health--disconnected { background: #fdeaec; color: #8a1220; }

/* A LIGHT panel inside a dark app, so it must set its own text colour. Without this every child
   that does not name a colour inherits the page's near-white and lands on a near-white card —
   which is exactly what happened to the title and the status pill: 1.02:1, invisible. Setting it
   on the container means anything added later inherits correctly instead of needing its own patch. */
.ptv-gate { padding: 2rem 1.5rem; border-radius: .75rem; background: var(--tp-grey-bg);
            color: var(--tp-text); text-align: center; }
.ptv-gate__pill { display: inline-flex; align-items: center; gap: .375rem; font-weight: 700; font-size: .8125rem; }
.ptv-gate__dot { width: .5rem; height: .5rem; border-radius: 50%; background: var(--ptv-primary); }
/* IPS-60: back from checkout, waiting for the grant — a status line, not a button. */
.ptv-gate__confirming { display: inline-flex; align-items: center; gap: .5rem; font-weight: 600; min-height: 44px; }
.ptv-gate__title { margin: .5rem 0; }
.ptv-gate__body, .ptv-gate__devices { color: #45454d; }
.ptv-gate__price { font-size: 1.5rem; font-weight: 700; margin: 1rem 0; }
.ptv-gate__countdown-abs { color: var(--tp-grey-600); }

/* The player leads the page, per the prototype: 16px corners and a deep drop into the plum.
   Grid, so the frame can be narrower than the column and centre itself in it — an upright clip is
   sized by its height (see .ptv-player__frame) and the ink either side of it is this background. */
.ptv-player { position: relative; display: grid; border-radius: 16px; overflow: hidden;
              background: var(--tp-ink-true); box-shadow: 0 20px 60px var(--tv-tile-shadow);
              /* Roughly the height of the player's own control bar. Overlays stop short of it so
                 the controls stay reachable while a card is up — see .ptv-player__unlockover. */
              --ptv-controlbar: 64px;
              /* The ceiling on the picture, so the control bar is always on screen with it. Past
                 860px a video stops gaining anything from more height on a large display. */
              --ptv-frame-max-height: min(78vh, 860px); }

/* Watch page, under the player: badge-and-title row, then the channel byline, then the copy. */
.ptv-watch__meta { margin-top: 22px; }
.ptv-watch__header { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.ptv-watch__header .ptv-watch__title { margin: 0; letter-spacing: -0.01em; }
.ptv-watch__byline { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
                     margin: 10px 0 0; font-size: 13.5px; color: var(--tv-text-dim); }
.ptv-watch__channel { display: inline-flex; align-items: center; gap: 10px;
                      color: var(--tv-text-dim); font-size: 13.5px; text-decoration: none; }
.ptv-watch__channel:hover { color: var(--ptv-text); }
.ptv-watch__channel-avatar { width: 26px; height: 26px; border-radius: 50%; flex: none;
                             display: inline-grid; place-items: center;
                             background: linear-gradient(135deg, var(--tv-avatar-deep), var(--pulse-violet));
                             color: var(--tp-white);
                             font-size: 10px; font-weight: 700; }
.ptv-watch__followers { font-family: var(--ptv-font-mono), ui-monospace, monospace;
                        font-size: 12px; color: var(--tv-link); }
/* The market a title is filed under. Sits between the byline and the copy, in the same faint
   mono the follower count uses — it is a filing fact, not a headline. */
.ptv-watch__region { display: flex; align-items: center; gap: 8px;
                     font-family: var(--ptv-font-mono), ui-monospace, monospace;
                     font-size: 12px; color: var(--tv-text-faint); margin: 10px 0 0; }

/* One flag, wherever it appears: the header's currency picker size, cropped to the box, with a
   hairline so a white-edged flag still has an edge on a dark ground. */
.ptv-flag { display: inline-block; width: 20px; height: 14px; object-fit: cover; border-radius: 3px;
            box-shadow: 0 0 0 1px var(--tv-hairline); vertical-align: middle; flex: none; }
.ptv-watch__description { font-size: 14.5px; line-height: 1.65; color: var(--tv-text-dim);
                          max-width: 660px; margin: 14px 0 0; }

/* The prototype's Follow pill: violet wash rising to the pressed wash when following/hovered. */
.ptv-follow { background: var(--tv-violet-soft); color: var(--tv-link);
    border: 1px solid var(--tv-violet-line); border-radius: 999px; padding: 5px 14px;
    font: inherit; font-size: 12px; font-weight: 700; cursor: pointer; transition: all 0.2s ease; }
.ptv-follow:hover { background: var(--tv-violet-press); }
.ptv-follow--on { background: var(--tv-violet-press); color: var(--tp-white); }

/* The watch page's shelf runs slightly tighter than the home rows, per the prototype. */
.ptv-watch .ptv-row { margin-block: 34px 0; }
.ptv-watch .ptv-row__title { font-size: 20px; margin-bottom: 14px; }
/* THE FRAME TAKES THE VIDEO'S OWN SHAPE. --ptv-aspect and --ptv-ratio are set by pulse-player.js
   from the poster and then from the decoded video; 16/9 is only what stands before either has been
   seen. A fixed 16/9 here was squeezing every upright clip into a strip down the middle of a black
   box, and throwing away most of a phone screen to show ink.

   One rule covers every shape, because the constraint is not portrait-versus-landscape — it is that
   the picture and its control bar have to fit on the screen together. Height is capped, width comes
   from that height and the video's own ratio, and the frame centres in whatever is left:

     16:9 in a 1100px column   → 1100 x 619, filling the width as it always has
     9:16 on a 900px viewport  →  395 x 702, upright, ink either side
     1:1                       →  702 x 702, rather than a 1100px square whose controls sit
                                             somewhere below the fold

   That last one is why this is not a portrait test. A square video is not portrait and was still
   taller than the screen. Nothing is ever letterboxed by us either: the ratio holds exactly, and the
   only thing that changes between shapes is which edge the video runs out of first. */
/* Continue watching: the replay opened where the viewer left it, with the way back to the start. */
.ptv-resume { position: absolute; top: 12px; left: 12px; z-index: 3; display: flex; align-items: center; gap: 10px;
              padding: 6px 8px 6px 12px; border-radius: 999px; font-size: 13px;
              background: var(--tv-tile-scrim); color: var(--tp-white); }
.ptv-resume__restart { border: 1px solid currentColor; border-radius: 999px; background: none; color: inherit;
                       font: inherit; font-weight: 600; padding: 3px 10px; cursor: pointer; }
.ptv-resume__restart:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: 2px; }
.ptv-player__frame { justify-self: center; width: 100%; aspect-ratio: var(--ptv-aspect, 16 / 9);
    max-width: calc(var(--ptv-frame-max-height) * var(--ptv-ratio, 1.7778)); }

/* The player fills the frame exactly, so the shape above is the shape on screen. Anything the two
   still disagree about — a rendition that is not quite the poster's ratio — the player letterboxes
   itself, into the same ink as the wrapper. */
.ptv-player__frame > mux-player { display: block; width: 100%; height: 100%; }
/* Pulse Studios advertising (A1): while a creative plays, the paused main player steps aside and
   the ad element takes the same frame. A hard swap on purpose — no fade, no transition — so the
   reduced-motion viewer and everyone else see the same thing: one picture, then the other. */
.ptv-player__frame[data-ptv-ad="1"] > mux-player:not(.ptv-ad-player) { display: none; }
.ptv-player__frame > .ptv-ad-player { transition: none; }
.ptv-player__status { padding: 1rem; color: var(--tp-white); background: rgba(0,0,0,.8); margin: 0; }
.ptv-player__status--failed { background: #45151b; }
.ptv-player__bandwidth { padding: .75rem 1rem; background: #fff4e5; color: #6b3b00; margin: 0; font-size: .875rem; }

.ptv-btn { display: inline-block; padding: .625rem 1.25rem; border-radius: 999px; font-weight: 700;
           border: 2px solid transparent; cursor: pointer; text-decoration: none; }
.ptv-btn--primary { background: var(--ptv-primary); color: var(--tp-white); }
/* The outline button lives on the DARK page far more often than not (header, devices, channel,
   subscription), and the brand violet on that ground is 2.79:1 — under the 4.5 text needs and the
   3.0 its own border needs as a control boundary. Lavender is the same brand family and clears
   both; the light panels below take the violet back. */
.ptv-btn--outline { background: transparent; color: var(--pulse-lavender); border-color: var(--pulse-lavender); }
.ptv-gate .ptv-btn--outline,
.ptv-purchase__card .ptv-btn--outline { color: var(--ptv-primary); border-color: var(--ptv-primary); }
.ptv-btn--link { background: none; border: none; color: var(--ptv-primary); text-decoration: underline; padding: 0; }
.ptv-btn:focus-visible { outline: 3px solid var(--ptv-accent); outline-offset: 2px; }
.ptv-btn[disabled] { opacity: .6; cursor: not-allowed; }

.ptv-library__items { list-style: none; padding: 0; }
.ptv-library__item { display: flex; justify-content: space-between; align-items: center; gap: 1rem;
                     padding: 1rem 0; border-bottom: 1px solid var(--ptv-surface-2); }
.ptv-library__thumb { flex: none; width: 128px; aspect-ratio: 16 / 9; border-radius: 8px; object-fit: cover;
                      background: linear-gradient(135deg, var(--ptv-surface-2), var(--ptv-surface)); }
.ptv-library__body { flex: 1; min-width: 0; }
.ptv-library__section { margin-block: 0 2.5rem; }
.ptv-library__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.ptv-library__empty { color: var(--ptv-muted); }
.ptv-library__clear, .ptv-library__remove { background: none; border: 0; padding: 0; font: inherit; font-weight: 600;
    color: var(--tv-link); text-decoration: underline; cursor: pointer; }
.ptv-library__clear:disabled, .ptv-library__remove:disabled { opacity: .5; cursor: default; }
.ptv-library__progress { height: 4px; border-radius: 999px; background: var(--ptv-surface-2); overflow: hidden; margin: .375rem 0; }
.ptv-library__progress > span { display: block; height: 100%; background: var(--ptv-primary); }
@media (max-width: 520px) { .ptv-library__thumb { width: 88px; } }
/* Same light-panel-in-a-dark-app rule as .ptv-gate above: the card names its own text colour so
   descendants cannot inherit the page's near-white onto it. */
.ptv-purchase__card { max-width: 32rem; padding: 1.5rem; border-radius: .75rem;
                      background: var(--tp-grey-bg); color: var(--tp-text); }
/* Sits on the dark shell, not on a card. A sign-in error the viewer cannot read is
   the worst possible one to lose. */
.ptv-signin__error { color: var(--ptv-danger); font-weight: 600; }
/* This one IS inside .ptv-purchase__card, which is light — dark red is correct there. */
.ptv-purchase__error { color: #8a1220; font-weight: 600; }
.ptv-signin input { display: block; width: 100%; max-width: 24rem; padding: .625rem; margin: .25rem 0 .5rem;
                    border: 2px solid #8a8a94; border-radius: .375rem; font-size: 1rem; }
.ptv-signin input:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: 1px; }

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden;
                   clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }

/* A pulsing dot beside video is precisely what this setting exists to stop. */
@media (prefers-reduced-motion: reduce) {
    .ptv-onair__track { animation: none; }
    .ptv-onair__viewport { overflow-x: auto; }
    .ptv-live--on .ptv-live__dot { animation: none; }
    *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---------------------------------------------------------------------------
   Landing page: sub-nav, hero, channels.

   Carried over from the merged section, which is where these were written. The
   prototype leads with a hero and a row of channels, and the pre-split page had
   neither — restoring the old app wholesale would have quietly reverted them.

   No viewer or follower counts anywhere: the only figure available is playback
   tokens issued, which is not a count of people.
   --------------------------------------------------------------------------- */

/* Channels — the prototype's channel cards, followers caption included (real records only). */
.ptv-channels { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }

.ptv-channel { display: flex; align-items: center; gap: 14px; padding: 16px;
               border-radius: 12px; background: var(--tv-surface, var(--ptv-surface));
               border: 1px solid var(--tv-hairline); }

/* The card as a link to the channel's own page. */
.ptv-channel--link { color: inherit; text-decoration: none; transition: all 0.2s ease; }
.ptv-channel--link:hover { transform: scale(1.02); background: var(--ptv-surface-2); }
.ptv-channel--link:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: 3px; }

/* Channel detail. The banner runs edge to edge like the home hero, on the brand gradient, with
   the channel's mark overhanging the fade into the page. */
.ptv-chan__banner { position: relative; min-height: 230px; display: flex; align-items: flex-end;
                    width: 100vw; margin-left: calc(50% - 50vw);
                    background: var(--brand-gradient); }
.ptv-chan__banner::after { content: ""; position: absolute; inset: auto 0 0 0; height: 110px;
                           background: linear-gradient(to top, var(--tv-bg), transparent);
                           pointer-events: none; }
/* The creator's cover sits UNDER the brand gradient's fade rather than replacing it, so the
   channel mark and name keep the same contrast whatever picture is chosen — a bright photo behind
   white type is the failure this scrim exists to prevent. */
.ptv-chan__cover { position: absolute; inset: 0; overflow: hidden; }
.ptv-chan__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ptv-chan__cover-scrim { position: absolute; inset: 0;
                         background: linear-gradient(to top, rgba(10,6,20,0.92) 0%,
                                                     rgba(10,6,20,0.55) 45%, rgba(10,6,20,0.35) 100%); }
.ptv-chan__head { position: relative; z-index: 1; display: flex; align-items: flex-end;
                  gap: 1.375rem; flex-wrap: wrap; width: 100%; max-width: 1200px;
                  margin: 0 auto; padding: 4rem 1.5rem 1.625rem; box-sizing: border-box; }
.ptv-chan__avatar { width: 104px; height: 104px; border-radius: 50%; overflow: hidden; flex: none;
                    display: grid; place-items: center; background: var(--ptv-surface-2);
                    border: 4px solid var(--tv-bg);
                    font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
                    font-size: 2rem; font-weight: 700; color: var(--ptv-text); }
.ptv-chan__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ptv-chan__ident { flex: 1; min-width: 16.25rem; }
.ptv-chan__back { display: inline-block; color: var(--ptv-muted); font-size: .78rem;
                  font-weight: 600; text-decoration: none; padding-bottom: .5rem; }
.ptv-chan__back:hover { color: var(--ptv-text); }
.ptv-chan__name { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
                  font-size: 2.25rem; letter-spacing: -.01em; margin: 0; }
.ptv-chan__meta { font-size: .8125rem; color: var(--ptv-muted); margin: .375rem 0 0; }
.ptv-chan__desc { font-size: .9rem; line-height: 1.65; color: var(--ptv-muted);
                  max-width: 40rem; margin: 1.625rem 0 1.125rem; }
.ptv-chan__missing { padding-top: 2rem; }

/* Search page. One big field in the display face, the chips beneath, the results in the same
   grid the shelves use. */
.ptv-searchpage__input { width: 100%; box-sizing: border-box; margin: 1.5rem 0 1.125rem;
    background: var(--tv-fill); border: 1px solid var(--tv-fill-border);
    border-radius: 16px; padding: 1.25rem 1.5rem; color: var(--ptv-text);
    font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 1.5rem; font-weight: 700; letter-spacing: -.01em; }
.ptv-searchpage__input::placeholder { color: var(--tv-text-faint); }
.ptv-searchpage__chips { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1.625rem; }
.ptv-chip { background: var(--tv-fill); color: var(--ptv-muted);
    border: 1px solid var(--tv-hairline); border-radius: 999px; padding: .5rem 1rem;
    font: inherit; font-size: .78rem; font-weight: 700; cursor: pointer;
    transition: border-color .2s ease, color .2s ease; }
.ptv-chip:hover { border-color: var(--tv-hover-border); color: var(--ptv-text); }
.ptv-chip--on { background: var(--ptv-primary); border-color: var(--ptv-primary); color: var(--tp-white); }
.ptv-searchpage__count { font-size: .78rem; color: var(--tv-text-faint); margin: 0 0 1rem; }
.ptv-searchpage__channels { margin-top: 2rem; }

.ptv-channel__logo { width: 52px; height: 52px; border-radius: 50%; object-fit: cover; flex: none; }

/* The prototype's mark: initials on the brand gradient, in the display face. */
.ptv-channel__logo--mark { display: grid; place-items: center;
    background: linear-gradient(135deg, var(--pulse-violet), var(--tp-purple));
    font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 17px; font-weight: 700; color: #fff; }

.ptv-channel__ident { min-width: 0; }

.ptv-channel__name { font-size: 15px; font-weight: 700; margin: 0; color: var(--ptv-text); }

.ptv-channel__desc { font-size: 12.5px; margin: 2px 0 0; color: var(--tv-text-faint); }

.ptv-channel__followers { font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 10.5px; margin: 4px 0 0; color: var(--tv-link); }

/* The sub-nav reads as tabs, as it does in the prototype. */
.ptv-subnav { display: flex; align-items: center; gap: .25rem; flex-wrap: wrap;
              padding: .5rem 0 1rem; }

.ptv-subnav__item { padding: .375rem .875rem; border-radius: 999px; text-decoration: none;
                    font-weight: 600; font-size: .9375rem; color: var(--ptv-muted); }

.ptv-subnav__item:hover { color: var(--ptv-text); background: var(--ptv-surface); }

.ptv-subnav__item--current { color: var(--ptv-text); background: var(--ptv-surface-2); }

.ptv-subnav__currency { margin-left: auto; }

.ptv-subnav__currency select { background: var(--ptv-surface); color: var(--ptv-text);
                               border: 1px solid var(--ptv-surface-2); border-radius: 999px;
                               padding: .375rem .75rem; font: inherit; }

/* Hero. */
.ptv-hero { position: relative; display: flex; align-items: flex-end; min-height: 380px;
    border-radius: 16px; overflow: hidden; margin-block: 0 2.5rem;
    /* Palette stops only: Deep Plum -> TP Purple -> Electric Violet (was off-palette #46108a). */
    background: linear-gradient(135deg, var(--pulse-plum) 0%, var(--tp-purple) 46%, rgba(106,0,255,.55) 100%); }
/* The prototype's hero runs edge to edge, breaking out of the 1200px column, with the artwork
   fading into the page rather than ending at a card edge. */
/* The ON AIR bar — the prototype's live ticker (mono label, pulsing-dot titles on the plum
   wash), with ticker-tape motion added: the run is laid twice and the track rolls half its own
   width, so the loop is seamless. Hover pauses it; reduced motion stops it and the bar falls
   back to the prototype's plain overflow scroll. */
.ptv-onair { display: flex; align-items: center; gap: 22px; padding: 9px 40px;
    width: 100vw; margin-left: calc(50% - 50vw);
    border-bottom: 1px solid var(--tv-hairline); background: var(--tv-onair-bg);
    white-space: nowrap; }
.ptv-onair__label { font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 10.5px; font-weight: 700; letter-spacing: 0.14em;
    color: var(--ptv-accent); flex: none; }
.ptv-onair__viewport { overflow: hidden; flex: 1; min-width: 0; }
.ptv-onair__track { display: inline-flex; animation: ptv-ticker 30s linear infinite; }
.ptv-onair__track:hover { animation-play-state: paused; }
.ptv-onair__run { display: inline-flex; align-items: center; gap: 22px; padding-right: 22px; }
.ptv-onair__item { display: inline-flex; align-items: center; gap: 8px;
    color: var(--tv-text-dim); text-decoration: none;
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11.5px;
    padding: 2px 0; flex: none; }
a.ptv-onair__item:hover { color: #fff; }
.ptv-onair__dot { width: 6px; height: 6px; border-radius: 50%;
    background: var(--ptv-accent); animation: ptv-pulse 1.6s ease infinite; }
.ptv-onair__zone { color: var(--tv-text-faint); letter-spacing: 0.1em; }
@keyframes ptv-ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (max-width: 640px) { .ptv-onair { padding: 8px 16px; gap: 14px; } }

.ptv-hero--bleed { width: 100vw; margin-left: calc(50% - 50vw); min-height: 460px;
    border-radius: 0; border-bottom: 10px solid var(--pulse-plum); }
.ptv-hero__art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ptv-hero__scrim { position: absolute; inset: 0;
    background: linear-gradient(105deg, var(--tv-hero-scrim-ink) 0%, var(--tv-hero-scrim-mid) 45%, var(--tv-hero-scrim-glow) 100%); }
/* The bottom of the artwork dissolves into the page background, per the prototype. */
.ptv-hero--bleed::after { content: ""; position: absolute; inset: auto 0 0 0; height: 140px;
    background: linear-gradient(to top, var(--tv-bg), transparent); pointer-events: none; }
/* Keeps the copy aligned with the 1200px content column the hero broke out of. */
.ptv-hero__inner { position: relative; width: 100%; max-width: 1200px; margin: 0 auto; z-index: 1; }
.ptv-hero__body { position: relative; padding: 2.375rem 2.625rem 3rem; max-width: 47.5rem; }

@media (max-width: 640px) {
    .ptv-hero__body { padding: 2rem 1.5rem; }
}

.ptv-hero__badges { display: flex; align-items: center; gap: .5rem; margin-bottom: .75rem; }

.ptv-hero__title { margin: 0 0 1rem; color: var(--ptv-text);
    font-size: clamp(2rem, 5vw, 3.5rem); line-height: 1.04; letter-spacing: -.02em; }

.ptv-hero__actions { display: flex; gap: .75rem; flex-wrap: wrap; margin: 0; }

.ptv-catalogue__title { color: var(--ptv-text); }

/* Channels page: the same tile as the landing row, laid out two up with room for the description. */
.ptv-channels--wide { grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr)); }
.ptv-channel--wide { align-items: flex-start; gap: 1.125rem; padding: 1.25rem; }
.ptv-channel--wide .ptv-channel__logo { width: 5.25rem; height: 5.25rem; border-radius: 12px; flex-shrink: 0; }
.ptv-channel--wide .ptv-channel__desc { font-size: .84rem; line-height: 1.55; color: var(--ptv-muted); margin: .375rem 0 0; }

/* Sits under the heading, not into it: headings carry no bottom margin here (global reset), so a
   negative top margin ran the second line of a wrapped title straight through this copy. */
.ptv-row__sub { color: var(--ptv-muted); font-size: .82rem; margin: .35rem 0 1rem; }
.ptv-row__action { float: right; font-size: .8rem; font-weight: 500; text-decoration: none;
    color: var(--ptv-text); border: 1px solid rgba(255, 255, 255, .18);
    border-radius: 999px; padding: .4rem .9rem; }
.ptv-row__action:hover { background: rgba(255, 255, 255, .08); }

/* Follow control on the channel banner — prototype's pill, filled when following. */
.ptv-chan__followers { font-family: var(--ptv-font-mono), ui-monospace, monospace; color: var(--tv-link); }
.ptv-chan__follow { background: rgba(106, 0, 255, .22); color: var(--tv-link);
                    border: 1px solid rgba(106, 0, 255, .5); border-radius: 999px;
                    padding: .6875rem 1.625rem; font: inherit; font-size: .875rem; font-weight: 700;
                    cursor: pointer; margin-bottom: .375rem; align-self: flex-end;
                    transition: background .2s ease, color .2s ease; }
.ptv-chan__follow:hover { background: rgba(106, 0, 255, .35); color: var(--tp-white); }
.ptv-chan__follow--on { background: var(--ptv-primary); color: var(--tp-white); }
.ptv-chan__follow[disabled] { opacity: .6; cursor: wait; }

/* FR-022 — the holding card: the prototype's plum letterbox with gold crop marks, drawn OVER the
   mounted player so the session is never torn down. Distinct from the circuit-reconnect overlay. */
/* Full-bleed, because it is a broadcast slate and a slate with a notch cut out of it is not one.
   pointer-events: none instead, so the controls underneath stay reachable — the card announces
   itself and nothing in it is clickable, and a viewer who wants to leave fullscreen or turn the
   sound down during an intermission should not have to wait for the programme to come back. */
.ptv-holdcard { position: absolute; inset: 0; z-index: 5; pointer-events: none;
    display: flex; flex-direction: column;
    align-items: center; justify-content: center; text-align: center; padding: 2.5rem 3.75rem;
    background: linear-gradient(180deg, var(--tv-holdcard-top) 0%, var(--tv-holdcard-mid) 55%, var(--tv-holdcard-top) 100%); }
.ptv-holdcard__corner { position: absolute; width: 22px; height: 22px;
    border-color: var(--tp-accent-gold); border-style: solid; border-width: 0; }
.ptv-holdcard__corner--tl { top: 1rem; left: 1rem; border-top-width: 2.5px; border-left-width: 2.5px; }
.ptv-holdcard__corner--tr { top: 1rem; right: 1rem; border-top-width: 2.5px; border-right-width: 2.5px; }
.ptv-holdcard__corner--bl { bottom: 1rem; left: 1rem; border-bottom-width: 2.5px; border-left-width: 2.5px; }
.ptv-holdcard__corner--br { bottom: 1rem; right: 1rem; border-bottom-width: 2.5px; border-right-width: 2.5px; }
.ptv-holdcard__word { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: clamp(1.75rem, 5vw, 2.75rem); font-weight: 700; letter-spacing: -.02em;
    color: var(--tp-white); margin: 0; }
.ptv-holdcard__line { font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: .75rem; letter-spacing: .1em; color: var(--tv-link); margin: .75rem 0 0; }
.ptv-holdcard__note { font-size: .78rem; color: var(--tv-text-faint); margin: .5rem 0 0; }

/* FR-034 — the unlock card's disclosure list. */
.ptv-gate__terms { list-style: none; margin: 0 auto 1.375rem; padding: 0; display: grid;
    gap: .5625rem; font-size: .84rem; color: var(--tp-black); max-width: 26rem; text-align: left; }
.ptv-gate__terms li { padding-left: 1.25rem; position: relative; }
.ptv-gate__terms li::before { content: "✓"; position: absolute; left: 0;
    color: var(--tp-on-teal-tint); font-weight: 700; }

/* US-001 — the territory gate's footnote and gold reason pill. */
.ptv-gate__pill--territory { color: var(--tp-on-gold-tint);
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: .7rem;
    letter-spacing: .08em; }
.ptv-gate__footnote { font-size: .72rem; color: var(--tp-grey-text); margin: .875rem 0; max-width: 26rem;
    margin-inline: auto; }

/* ACC-004 — caption appearance settings under the player. */
.ptv-captions { display: flex; gap: 1rem; flex-wrap: wrap; padding: .625rem .875rem;
    background: var(--ptv-surface); font-size: .78rem; color: var(--ptv-muted); }
.ptv-captions__field { display: inline-flex; align-items: center; gap: .5rem; }
.ptv-captions__field select { background: var(--ptv-surface-2); color: var(--ptv-text);
    border: 1px solid var(--tv-hairline); border-radius: 8px; padding: .25rem .5rem; font: inherit; }

.ptv-entitlement__receipt { font-size: .78rem; color: var(--ptv-muted); white-space: nowrap; }

.ptv-purchase__consent { display: flex; gap: .5rem; align-items: flex-start; margin: .75rem 0;
    font-size: .82rem; color: var(--ptv-muted); }
.ptv-purchase__consent input { margin-top: .2rem; }

/* FR-036/037 — the live-room panel: a 330px glass column ADJACENT to the player (never over
   the picture), tabbed Chat · Polls on lives, Shop everywhere, and collapsible — the fold gives
   the player the full row back. On narrow screens it drops below the player instead. */
.ptv-stage { display: flex; align-items: stretch; gap: 14px; }
.ptv-stage > .ptv-player { flex: 1; min-width: 0; align-self: flex-start; width: 100%; }
.ptv-room { width: 330px; flex: none;
    display: flex; flex-direction: column; min-height: 420px;
    background: var(--tv-room-glass); backdrop-filter: blur(16px);
    border: 1px solid var(--tv-room-edge); border-radius: 12px; overflow: hidden; }

/* The fold: a slim rail that keeps the way back in reach. */
.ptv-room__reopen { flex: none; align-self: stretch; width: 44px;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
    background: var(--tv-room-glass); border: 1px solid var(--tv-room-edge); border-radius: 12px;
    color: var(--tv-text-dim); font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; }
.ptv-room__reopen:hover { color: var(--tp-white); }
.ptv-room__reopenword { writing-mode: vertical-rl; letter-spacing: 0.08em; }
.ptv-room__collapse { margin-left: auto; flex: none; width: 30px;
    background: transparent; color: var(--tv-text-dim); border: 0; border-radius: 8px;
    font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; }
.ptv-room__collapse:hover { color: var(--tp-white); background: var(--tv-pill-hover); }

@media (max-width: 960px) {
    .ptv-stage { flex-direction: column; }
    .ptv-room { width: auto; min-height: 320px; }
    .ptv-room__reopen { width: auto; flex-direction: row; padding: 10px; }
    .ptv-room__reopenword { writing-mode: horizontal-tb; }
}

.ptv-room__tabs { display: flex; gap: 2px; padding: 8px; border-bottom: 1px solid var(--tv-hairline); }
.ptv-room__tab { flex: 1; background: transparent; color: var(--tv-text-dim); border: 0;
    border-radius: 8px; padding: 8px; font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; }
.ptv-room__tab--on { background: var(--tv-violet-press); color: var(--tp-white); }

.ptv-room__lines { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column;
    gap: 10px; font-size: 13px; line-height: 1.45; }
.ptv-room__line { display: flex; gap: 8px; }
.ptv-room__dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex: none; }
.ptv-room__text { color: var(--tv-text-dim); }
/* Razor trims the space after the name; an untagged line needs the gap the role tag otherwise gives. */
.ptv-room__line b + .ptv-room__text { margin-left: 5px; }
.ptv-room__line--removed em { color: var(--ptv-muted); opacity: .7; }

.ptv-room__composer { padding: 10px; border-top: 1px solid var(--tv-hairline); display: flex; gap: 8px; }
.ptv-room__composer input { flex: 1; min-width: 0; background: var(--tv-control-fill);
    border: 1px solid var(--tv-fill-border); border-radius: 999px; padding: 9px 14px;
    color: var(--tp-white); font: inherit; font-size: 13px; }
.ptv-room__send { background: var(--brand-primary); border: 0; border-radius: 999px;
    width: 36px; height: 36px; flex: none; display: grid; place-items: center; color: var(--tp-white); cursor: pointer; }
.ptv-room__send:hover { background: var(--brand-primary-dark); }
.ptv-room__off, .ptv-room__error { font-size: 12px; color: var(--ptv-muted); margin: 0; padding: 10px 12px; }
.ptv-room__off a { color: var(--tv-link); font-weight: 600; }
.ptv-room__retry { background: none; border: 0; padding: 0; margin-left: 4px; font: inherit;
    color: var(--tv-link); font-weight: 600; text-decoration: underline; cursor: pointer; }

.ptv-room__body { flex: 1; overflow-y: auto; padding: 16px; }
.ptv-room__polltag { font-size: 11px; font-weight: 700; letter-spacing: 0.08em;
    color: var(--tv-link); margin-bottom: 8px; }
.ptv-room__question { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 17px; margin: 0 0 14px; color: var(--ptv-text); }
.ptv-room__options { display: grid; gap: 9px; }
.ptv-room__option { position: relative; overflow: hidden; text-align: left;
    background: var(--tv-fill); border: 1px solid var(--tv-fill-border); border-radius: 8px;
    padding: 11px 13px; color: var(--tp-white); font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer; }
.ptv-room__option:hover { border-color: var(--tv-hover-border); }
.ptv-room__option:disabled { cursor: default; }
.ptv-room__fill { position: absolute; inset: 0 auto 0 0; background: var(--tv-violet-wash);
    transition: width 0.5s ease; }
.ptv-room__optrow { position: relative; display: flex; justify-content: space-between; gap: 8px; }
.ptv-room__tally { font-family: var(--ptv-font-mono), ui-monospace, monospace; color: var(--tv-text-dim); }

.ptv-room__shop { display: grid; gap: 12px; align-content: start; padding: 14px; }
.ptv-room__shopitem { display: flex; gap: 12px; background: var(--tv-shop-fill);
    border: 1px solid var(--tv-hairline); border-radius: 12px; padding: 12px; }
.ptv-room__shoptile { width: 56px; height: 56px; border-radius: 8px; flex: none; object-fit: cover; }
.ptv-room__shoptile--glyph { display: grid; place-items: center;
    background: linear-gradient(135deg, var(--pulse-violet), var(--tp-purple));
    font-family: var(--ptv-font-display), 'DM Sans', sans-serif; font-weight: 700; font-size: 15px; color: var(--tp-white); }
.ptv-room__shopbody { flex: 1; min-width: 0; }
.ptv-room__shopname { display: block; font-size: 13px; font-weight: 700; color: var(--ptv-text); }
.ptv-room__shopprice { display: block; font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 12px; color: var(--tv-text-dim); margin: 3px 0 7px; }
.ptv-room__shopname:last-child { margin-bottom: 7px; }
.ptv-room__shopbody .ptv-room__shopname + .ptv-room__shopadd,
.ptv-room__shopbody .ptv-room__shopname + .ptv-room__shopactions { margin-top: 7px; }
/* Add to basket and Buy now side by side; they wrap on a narrow tray rather than overflow. */
.ptv-room__shopactions { display: flex; flex-wrap: wrap; gap: 6px; }
.ptv-room__shopadd { display: inline-block; background: var(--tv-violet-wash); color: var(--tv-link);
    border: 1px solid var(--tv-violet-line); border-radius: 999px; padding: 5px 13px;
    font-size: 11.5px; font-weight: 700; text-decoration: none; }
.ptv-room__shopadd:hover { background: var(--tv-violet-press); color: var(--tp-white); }
/* Buy now is the filled one of the pair: the direct route to the checkout. */
.ptv-room__shopadd--buy { background: var(--tv-violet-press); color: var(--tp-white); border-color: transparent; }
.ptv-room__shopadd--buy:hover { background: var(--pulse-violet); color: var(--tp-white); }
.ptv-room__shopnote { font-size: 11px; color: var(--tv-text-faint); margin: 2px 2px 0; }

/* The BETA sticker beside the lockup — InCtrl's Beta Sticker flag decides whether it shows. */
.ptv-beta { flex: none; font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 10px; font-weight: 700; letter-spacing: 0.1em;
    color: var(--tp-accent-gold); background: var(--tv-beta-bg);
    border: 1px solid var(--tv-beta-line);
    padding: 3px 8px; border-radius: 999px; margin-left: -12px; }

/* The unlock card OVER the playing intro reel — the prototype's overlay veil. */
/* The veil covers the reel but NOT the control bar, and passes clicks through everywhere the card
   itself is not. Full-bleed with pointer events, it sat on top of play, volume and fullscreen — the
   viewer could see the intro playing and could not pause it or make it bigger. */
.ptv-player__unlockover { position: absolute; inset: 0 0 var(--ptv-controlbar) 0; z-index: 6;
    display: flex; align-items: center; justify-content: center;
    background: var(--tv-unlock-veil); backdrop-filter: blur(3px);
    overflow-y: auto; padding: 1rem; pointer-events: none; }
.ptv-player__unlockover > * { pointer-events: auto; }

/* Follow chips on the channel cards: the card is a link, the chip floats over it, outside it. */
.ptv-channel-holder { position: relative; }
.ptv-channel-holder .ptv-channel { padding-right: 96px; }
.ptv-channel__follow { position: absolute; top: 14px; right: 14px; }

/* The channel page's story strip: the creator's pictures with their captions. */
.ptv-chan__gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 14px; margin: 22px 0 6px; }
.ptv-chan__shot { margin: 0; }
.ptv-chan__shot img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: 12px;
    box-shadow: 0 2px 12px var(--tv-tile-shadow); display: block; }
.ptv-chan__shot figcaption { font-size: 12px; color: var(--tv-text-faint); margin-top: 6px; }

.ptv-chan__shot figcaption { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ptv-chan__like { background: var(--tv-control-fill); color: var(--tv-text-dim);
    border: 1px solid var(--tv-control-border); border-radius: 999px; padding: 3px 10px;
    font: inherit; font-size: 11.5px; font-weight: 700; cursor: pointer; flex: none;
    transition: all 0.2s ease; }
.ptv-chan__like:hover { color: var(--tp-white); border-color: var(--tv-control-border-hover); }
.ptv-chan__like--on { color: var(--ptv-accent); border-color: var(--ptv-accent); }

/* Long unbroken channel names (dev's email-named studios) must stop short of the chip. */
.ptv-channel-holder .ptv-channel__name { overflow-wrap: anywhere; }

/* The channel's activity grid — day × UTC hour, coloured by how much really happened. */
.ptv-heat { margin: 26px 0 6px; }
.ptv-heat__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 20px; margin: 0 0 4px; }
.ptv-heat__sub { font-size: 12.5px; color: var(--tv-text-faint); margin: 0 0 12px; }
.ptv-heat__grid { display: grid; grid-template-columns: 34px repeat(24, 1fr); gap: 3px;
    max-width: 720px; }
.ptv-heat__hour, .ptv-heat__day { font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 9.5px; color: var(--tv-text-faint); align-self: center; }
.ptv-heat__day { text-align: right; padding-right: 4px; }
.ptv-heat__cell { display: block; aspect-ratio: 1; border-radius: 3px; background: var(--tv-heat-0); }
.ptv-heat__cell--1 { background: var(--tv-heat-1); }
.ptv-heat__cell--2 { background: var(--tv-heat-2); }
.ptv-heat__cell--3 { background: var(--tv-heat-3); }
.ptv-heat__cell--4 { background: var(--tv-heat-4); }
.ptv-heat__legend { display: flex; align-items: center; gap: 5px; margin: 10px 0 0;
    font-size: 11px; color: var(--tv-text-faint); }
.ptv-heat__legend .ptv-heat__cell { width: 12px; }
@media (max-width: 640px) { .ptv-heat__grid { max-width: none; } }

/* ---------------------------------------------------------------------------
   Series: the shelf and one season's page.

   The season page follows the shape a viewer already knows from a television
   app — artwork and pitch at the top, one Play button, then the whole episode
   list in running order.

   The rule the styling has to carry: an episode that is not out yet is SHOWN,
   dimmed, and unmistakably not a control. Dimming alone would be a colour-only
   signal, which §9 forbids and which nobody reading in Caribbean daylight can
   see — so the disabled row also loses the hover lift, loses the pointer, and
   says its state in words where the runtime would be.
   --------------------------------------------------------------------------- */

.ptv-series__search { margin-top: 1.25rem; font-size: 1.125rem; padding: .9rem 1.25rem; }

.ptv-series-grid { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 18px; }

.ptv-series-card { display: block; color: inherit; text-decoration: none;
    transition: transform .2s ease; }
.ptv-series-card:hover { transform: translateY(-3px); }
.ptv-series-card:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: 4px;
    border-radius: 14px; }

.ptv-series-card__art { position: relative; display: block; aspect-ratio: 2 / 3;
    border-radius: 14px; overflow: hidden; background: var(--tv-surface);
    box-shadow: 0 4px 18px var(--tv-tile-shadow); }
.ptv-series-card__art img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Never a bare block: a season with no key art shows its own name instead. */
.ptv-series-card__art--ph { position: absolute; inset: 0; display: grid; place-items: center;
    padding: 1rem; text-align: center; background: var(--brand-gradient);
    font-family: var(--ptv-font-display), 'DM Sans', sans-serif; font-size: 1.05rem;
    font-weight: 700; line-height: 1.3; color: var(--tp-white); }
.ptv-series-card__scrim { position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(to top, var(--tv-card-scrim) 0%, transparent 55%); }
.ptv-series-card__pill { position: absolute; left: 10px; bottom: 10px;
    background: var(--tv-pill-chrome); border: 1px solid var(--tv-fill-border);
    border-radius: 999px; padding: 3px 10px; font-size: 11.5px; font-weight: 700; }

.ptv-series-card__body { display: block; padding: 10px 2px 0; }
.ptv-series-card__name { display: block; font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 1rem; font-weight: 700; line-height: 1.3; overflow-wrap: anywhere; }
.ptv-series-card__by { display: block; font-size: 12.5px; color: var(--tv-text-dim); margin-top: 3px; }
.ptv-series-card__count { display: block; font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 11.5px; color: var(--tv-text-faint); margin-top: 4px; }

/* One season's hero — full-bleed artwork under a scrim, like the home page's. */
.ptv-series__hero { position: relative; width: 100vw; margin-left: calc(50% - 50vw);
    min-height: 22rem; display: flex; align-items: flex-end; overflow: hidden; }
.ptv-series__art { position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; }
.ptv-series__scrim { position: absolute; inset: 0;
    background: linear-gradient(to top, var(--tv-hero-scrim-ink) 0%,
        var(--tv-hero-scrim-mid) 55%, var(--tv-hero-scrim-glow) 100%); }
.ptv-series__inner { position: relative; z-index: 1; width: 100%; max-width: 1200px;
    margin: 0 auto; padding: 4.5rem 1.5rem 2rem; box-sizing: border-box; }
.ptv-series__back { display: inline-block; color: var(--tv-text-dim); font-size: .78rem;
    font-weight: 600; text-decoration: none; padding-bottom: .5rem; }
.ptv-series__back:hover { color: var(--ptv-text); }
.ptv-series__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: clamp(2rem, 5vw, 3rem); letter-spacing: -.02em; margin: 0; }
.ptv-series__meta { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
    font-size: .8125rem; color: var(--tv-text-dim); margin: .625rem 0 0; }
.ptv-series__channel { color: var(--tv-link); text-decoration: none; font-weight: 600; }
.ptv-series__channel:hover { text-decoration: underline; }
.ptv-series__pill { background: var(--tv-hero-pill-chrome); border: 1px solid var(--tv-fill-border);
    border-radius: 999px; padding: 2px 10px; font-weight: 700; color: var(--ptv-text); }
.ptv-series__blurb { max-width: 44rem; font-size: .95rem; line-height: 1.6;
    color: var(--tv-text-dim); margin: 1rem 0 0; }
.ptv-series__actions { display: flex; align-items: center; flex-wrap: wrap; gap: .75rem;
    margin: 1.25rem 0 0; }
.ptv-series__nothing { font-size: .875rem; color: var(--tv-text-faint); }
.ptv-series__passnote { font-size: .8125rem; color: var(--tv-text-faint); margin: .625rem 0 0; }
.ptv-series__tags { display: flex; flex-wrap: wrap; gap: .375rem; margin: .875rem 0 0; }

.ptv-series__episodes { max-width: 1200px; margin: 2rem auto 0; }

.ptv-eps { list-style: none; margin: 1rem 0 0; padding: 0; }
.ptv-eps__item + .ptv-eps__item { border-top: 1px solid var(--tv-hairline); }

.ptv-eps__row { display: flex; align-items: center; gap: 1rem; padding: .875rem .75rem;
    border-radius: 12px; color: inherit; text-decoration: none; }
a.ptv-eps__row:hover { background: var(--tv-fill); }
a.ptv-eps__row:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: -2px; }

/* Not out yet. Dimmed AND inert AND labelled — three signals, because dimming
   on its own is a colour-only one. */
.ptv-eps__row--disabled { opacity: .48; cursor: not-allowed; }
.ptv-eps__row--disabled:hover { background: none; }

/* The episode's still: a small 16:9 tile ahead of the number, the same surface the cards use so
   a row without one (not out yet, still processing) reads as a tile and not a hole. */
.ptv-eps__thumb { flex: none; width: 8.5rem; aspect-ratio: 16/9; border-radius: 8px; overflow: hidden;
    background: var(--ptv-surface-2); box-shadow: 0 2px 10px var(--tv-shade-35); }
.ptv-eps__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.ptv-eps__thumb--ph { display: block; width: 100%; height: 100%;
    background: var(--tv-art-placeholder);
    box-shadow: inset 0 0 0 1px var(--tv-hairline); }
.ptv-eps__no { font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 12px;
    color: var(--tv-text-faint); min-width: 3.75rem; flex: none; }
.ptv-eps__body { flex: 1; min-width: 0; }
.ptv-eps__title { display: block; font-weight: 700; font-size: .975rem; overflow-wrap: anywhere; }
.ptv-eps__desc { display: block; font-size: .8125rem; line-height: 1.55; color: var(--tv-text-dim);
    margin-top: .25rem; }
.ptv-eps__state { display: flex; align-items: center; gap: .5rem; flex: none;
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11.5px;
    color: var(--tv-text-faint); text-align: right; }
.ptv-eps__cc { border: 1px solid var(--tv-fill-border); border-radius: 4px; padding: 0 4px;
    font-weight: 700; }
/* The date an episode arrives is the whole point of showing a locked row, so it keeps
   the readable colour the dimmed row would otherwise take it below. */
.ptv-eps__when { color: var(--tv-text-dim); font-weight: 700; }

@media (max-width: 640px) {
    .ptv-series__hero { min-height: 17rem; }
    .ptv-eps__row { flex-wrap: wrap; gap: .5rem 1rem; }
    .ptv-eps__thumb { width: 6rem; }
    /* Under the title: past the still, its gap, the number and its gap. */
    .ptv-eps__state { width: 100%; justify-content: flex-start; padding-left: 11.75rem; }
}

/* An episode's way back to its season, under the watch page's meta block. */
.ptv-watch__series { margin: 1rem 0 0; }

/* The browse region filter, shared by the series shelf and the on-demand library. Sits on the
   dark shell in the same soft fill the search field and chips use. */
.ptv-regionfilter { display: flex; align-items: center; gap: .5rem; margin: 0 0 1rem; }
.ptv-regionfilter__flag { margin-right: -.125rem; }
/* In a row heading the control sits beside the title rather than above the shelf, and carries no
   bottom margin of its own — the heading already owns that space. */
.ptv-row__control { float: right; }
.ptv-row__control .ptv-regionfilter { margin: 0; }
.ptv-row__control .ptv-regionfilter__select { padding: .3rem .8rem; font-size: .8rem; }
.ptv-regionfilter__label { font-family: var(--ptv-font-mono), ui-monospace, monospace;
    font-size: 11.5px; letter-spacing: .04em; text-transform: uppercase;
    color: var(--tv-text-faint); }
.ptv-regionfilter__select { background: var(--tv-fill); color: var(--ptv-text);
    border: 1px solid var(--tv-fill-border); border-radius: 999px;
    padding: .45rem 1rem; font: inherit; font-size: .875rem; font-weight: 600; }
.ptv-regionfilter__select:hover { border-color: var(--tv-hover-border); }
.ptv-regionfilter__select:focus-visible { outline: 3px solid var(--ptv-primary); outline-offset: 2px; }
/* The native menu is drawn by the OS in its own colours; naming them keeps the options legible
   where a dark page would otherwise leave white-on-white. */
.ptv-regionfilter__select option { background: var(--tv-surface); color: var(--ptv-text); }

/* ─── Share ────────────────────────────────────────────────────────────────────────────────
   The row under the title block. Built from the same violet tokens as .ptv-follow beside it,
   because these are the same kind of control in the same place — a row of chips that should
   read as one family rather than a brand parade. Deliberately NOT each platform's own colour:
   six brand colours under a title is a toolbar, and the eye goes to it instead of the video. */

.ptv-share { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-block: 18px 0; }

.ptv-share__label { font-size: 12px; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; color: var(--tv-muted, #9c93b5); margin-right: 2px; }

.ptv-share__btn { background: var(--tv-violet-soft); color: var(--tv-link);
    border: 1px solid var(--tv-violet-line); border-radius: 999px; padding: 5px 14px;
    font: inherit; font-size: 12px; font-weight: 700; cursor: pointer; text-decoration: none;
    display: inline-flex; align-items: center; gap: 6px; transition: all 0.2s ease; }

.ptv-share__btn:hover { background: var(--tv-violet-press); }

/* The share sheet is the only route to TikTok and Instagram, so it is the one that reads as the
   primary action rather than sitting level with the named links. */
.ptv-share__btn--native { background: var(--tv-violet-press); color: var(--tp-white); }

@media (max-width: 640px) {
    /* On a phone the sheet is what people reach for and the named links are the fallback, so it
       takes the full row rather than being one chip among seven wrapping badly. */
    .ptv-share__btn--native { flex: 1 1 100%; justify-content: center; }
}

/* IPS-66 — the season-pass upsell line on cards and the unlock card. Tokens only. */
.ptv-card__upsell { color: var(--ptv-muted); font-size: 12px; margin: 3px 0 0; }
.ptv-gate__upsell { margin: 1rem auto 0; font-size: .9rem; color: var(--ptv-muted); display: grid; gap: .5rem; justify-items: center; }
.ptv-gate__upsell p { margin: 0; }

/* IPS-70 — the CC toggle: a real button, 44px target, pressed state carried by words and aria-pressed. */
.ptv-cc { min-height: 44px; min-width: 44px; margin: .5rem 0; }
.ptv-cc[aria-pressed="true"] { font-weight: 700; }

/* IPS-73 — the receipt: a printable page. Tokens only; print hides the chrome, keeps the paper trail. */
.ptv-receipt { max-width: 640px; margin: 0 auto; }
.ptv-receipt__rows { display: grid; grid-template-columns: max-content 1fr; gap: .35rem 1rem; margin: 1rem 0; }
.ptv-receipt__rows dt { color: var(--ptv-muted); }
.ptv-receipt__rows dd { margin: 0; }
.ptv-receipt__legal { color: var(--ptv-muted); font-size: .85rem; margin-top: 1.5rem; }
.ptv-entitlement__receipt { color: var(--ptv-muted); }
@media print {
  .ptv-header, .ptv-footer, .ptv-receipt__actions { display: none !important; }
  .ptv-receipt { max-width: none; }
}
/* IPS-121: role tags in the live room — a word in a small pill beside the name, never colour alone. */
.ptv-room__role { display: inline-block; margin: 0 6px 0 4px; padding: 1px 7px; border-radius: 999px;
    font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    border: 1px solid var(--tv-hairline); color: var(--tv-text-dim); vertical-align: middle; }
.ptv-room__role--host { background: var(--brand-primary); border-color: transparent; color: var(--tp-white); }
.ptv-room__role--passholder { border-color: var(--ptv-focus); color: var(--ptv-focus); }
.ptv-room__role--ticketholder { border-color: var(--ptv-success); color: var(--ptv-success); }

/* IPS-123: the pinned question — at the top of the room, and over the picture. The stage is the
   positioning context so the overlay sits on the player, clear of the control bar. */
.ptv-stage { position: relative; }
.ptv-room__pinned { margin: 10px 12px 0; padding: 8px 10px; border-radius: 10px; font-size: 13px; line-height: 1.4;
    background: var(--tv-control-fill); border-left: 3px solid var(--ptv-focus); color: var(--ptv-text); }
.ptv-room__pinnedtag, .ptv-pinned-overlay__tag { display: inline-block; margin-right: 8px; font-size: 10px; font-weight: 700;
    letter-spacing: 0.08em; text-transform: uppercase; color: var(--ptv-focus); }
.ptv-pinned-overlay { position: absolute; left: 14px; bottom: calc(var(--ptv-controlbar, 64px) + 14px); z-index: 3;
    max-width: min(60%, 520px); padding: 8px 12px; border-radius: 10px; font-size: 14px; line-height: 1.4;
    background: var(--tv-hero-pill-chrome); border: 1px solid var(--tv-hairline); border-left: 3px solid var(--ptv-focus);
    color: var(--ptv-text); box-shadow: var(--tp-shadow-card); pointer-events: none; }
.ptv-pinned-overlay__who { font-weight: 700; margin-right: 6px; }
@media (max-width: 900px) { .ptv-pinned-overlay { position: static; max-width: none; margin: 0 0 10px; } }

/* IPS-124: slow mode, stated above the composer before anyone runs into it. */
.ptv-room__slow { font-size: 12px; color: var(--ptv-muted); margin: 0; padding: 8px 12px 0; }

/* IPS-120: the pinned product's card over the stream — top-right of the picture, one at a time. */
.ptv-shop-pin { position: absolute; right: 358px; top: 14px; z-index: 3; display: flex; gap: 10px; align-items: center;
    max-width: 320px; padding: 10px 12px; border-radius: 12px;
    background: var(--tv-hero-pill-chrome); border: 1px solid var(--tv-hairline); border-left: 3px solid var(--ptv-focus);
    color: var(--ptv-text); box-shadow: var(--tp-shadow-card); }
.ptv-shop-pin__tile { width: 44px; height: 44px; border-radius: 8px; object-fit: cover; flex: none; }
.ptv-shop-pin__body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ptv-shop-pin__tag { font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ptv-focus); }
.ptv-shop-pin__name { font-size: 13px; font-weight: 700; }
@media (max-width: 900px) { .ptv-shop-pin { position: static; max-width: none; margin: 0 0 10px; } }

/* ═══ PulseTV Explore (plan §5): ported verbatim from prototypes/explore/explore.css — the Explore
   page, filter bar, active chips, pager, category hero, sideways chips, channel-page chips, phone
   rules and the filter sheet. Tokens only. ═══════════════════════════════════════════════════ */
/* ─── Explore page ──────────────────────────────────────────────────────────────────────── */
.ptv-explore__head { display: flex; align-items: flex-end; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap; margin: .5rem 0 1.5rem; }
.ptv-explore__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 2.25rem; letter-spacing: -.01em; margin: 0; }
.ptv-explore__sub { color: var(--ptv-muted); font-size: .9rem; margin: .25rem 0 0; }
.ptv-explore__section { font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 1.125rem; margin: 2rem 0 .875rem; }

/* Category tiles. A grid like every other row (no carousels, owner decision 2026-10-05). The
   still is darkened under the name; the category's accent token draws the left edge. */
.ptv-cats { list-style: none; padding: 0; margin: 0 0 .5rem; display: grid; gap: .75rem;
    grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr)); }
.ptv-cat { position: relative; display: flex; flex-direction: column; justify-content: flex-end;
    aspect-ratio: 16 / 8; border-radius: 12px; overflow: hidden; text-decoration: none;
    color: var(--ptv-text); background: var(--tv-art-placeholder);
    box-shadow: inset 3px 0 0 var(--cat-accent, var(--ptv-primary)), 0 2px 12px var(--tv-shade-40);
    transition: transform .2s ease, box-shadow .2s ease; }
.ptv-cat:hover { transform: scale(1.03); box-shadow: inset 3px 0 0 var(--cat-accent, var(--ptv-primary)), 0 10px 34px var(--tv-card-glow); }
.ptv-cat:focus-visible { outline: 3px solid var(--ptv-focus); outline-offset: 3px; }
.ptv-cat__art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .55; }
.ptv-cat__scrim { position: absolute; inset: 0; background: linear-gradient(to top, var(--tv-tile-scrim) 10%, var(--tv-tile-scrim-fade) 100%); }
.ptv-cat__name { position: relative; margin: 0 .875rem; font-family: var(--ptv-font-display), 'DM Sans', sans-serif;
    font-size: 1.0625rem; font-weight: 700; line-height: 1.2; }
.ptv-cat__count { position: relative; margin: .125rem .875rem .75rem; color: var(--tv-text-dim);
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11.5px; font-weight: 500; }
.ptv-cat--channel .ptv-cat__count::after { content: " · channel"; color: var(--tv-text-faint); }
.ptv-cat[aria-current="true"] { outline: 2px solid var(--ptv-primary); outline-offset: 2px; }

/* ─── Filter bar ───────────────────────────────────────────────────────────────────────────
   One row of pill controls on the soft fill the search field and region filter already use.
   Each multi-choice filter is a button that opens a small menu of checkboxes with counts. */
.ptv-filters { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
    padding: .875rem 0; margin-top: .75rem; border-top: 1px solid var(--tv-hairline); }
.ptv-filters__sort { margin-left: auto; }
.ptv-filter { position: relative; }
.ptv-filter__btn, .ptv-filter__select {
    display: inline-flex; align-items: center; gap: .4rem;
    background: var(--tv-fill); color: var(--ptv-text);
    border: 1px solid var(--tv-fill-border); border-radius: 999px;
    padding: .45rem .95rem; font: inherit; font-size: .84rem; font-weight: 600; cursor: pointer;
    transition: border-color .2s ease; }
.ptv-filter__btn:hover, .ptv-filter__select:hover { border-color: var(--tv-hover-border); }
.ptv-filter__btn[aria-expanded="true"] { border-color: var(--tv-hover-border); }
.ptv-filter__select option { background: var(--tv-surface); color: var(--ptv-text); }
.ptv-filter__btn--on { background: var(--tv-violet-wash); border-color: var(--tv-violet-line); }
.ptv-filter__n { font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11px;
    background: var(--ptv-primary); color: var(--tp-white); border-radius: 999px; padding: 0 .4rem; }
.ptv-filter__chev { font-size: 10px; color: var(--ptv-muted); }
.ptv-filter__menu { position: absolute; z-index: 30; top: calc(100% + .375rem); left: 0; min-width: 13.5rem;
    background: var(--tv-surface); border: 1px solid var(--tv-fill-border); border-radius: 12px;
    padding: .375rem; box-shadow: 0 14px 40px var(--tv-shade-40); }
.ptv-filter__menu[hidden] { display: none; }
.ptv-filter__opt { display: flex; align-items: center; gap: .625rem; padding: .55rem .625rem;
    border-radius: 8px; font-size: .86rem; cursor: pointer; color: var(--ptv-text); }
.ptv-filter__opt:hover { background: var(--tv-fill); }
.ptv-filter__opt input { accent-color: var(--ptv-primary); width: 16px; height: 16px; }
.ptv-filter__count { margin-left: auto; color: var(--tv-text-faint);
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11.5px; }
.ptv-filter__btn--toggle[aria-pressed="true"] { background: var(--ptv-primary); border-color: var(--ptv-primary); color: var(--tp-white); }

/* Behind an open menu: a click anywhere else closes it (the prototype's document listener). */
.ptv-filter__veil { position: fixed; inset: 0; z-index: 29; }

/* Mobile: the bar folds into one "Filters · 3" button and a sheet. */
.ptv-filters__open { display: none; }

/* Active filters, each removable, then Clear all. */
.ptv-active { display: flex; flex-wrap: wrap; align-items: center; gap: .375rem; margin: 0 0 .875rem; }
.ptv-active__chip { display: inline-flex; align-items: center; gap: .375rem;
    background: var(--tv-violet-soft); color: var(--ptv-text); border: 1px solid var(--tv-violet-line);
    border-radius: 999px; padding: .3rem .4rem .3rem .75rem; font: inherit; font-size: .78rem; font-weight: 600; cursor: pointer; }
.ptv-active__chip span[aria-hidden] { display: inline-grid; place-items: center; width: 18px; height: 18px;
    border-radius: 50%; background: var(--tv-fill); font-size: 12px; }
.ptv-active__chip:hover { border-color: var(--tv-hover-border); }
.ptv-active__clear { background: none; border: 0; color: var(--tv-link); font: inherit; font-size: .8rem;
    font-weight: 700; cursor: pointer; padding: .3rem .25rem; }

.ptv-explore__count { font-size: .8rem; color: var(--tv-text-faint); margin: 0 0 1rem; }
.ptv-explore__empty { text-align: center; padding: 3rem 1rem; color: var(--ptv-muted);
    border: 1px dashed var(--tv-fill-border); border-radius: 16px; }
.ptv-explore__empty h2 { font-family: var(--ptv-font-display), 'DM Sans', sans-serif; color: var(--ptv-text); font-size: 1.25rem; margin: 0 0 .375rem; }

/* ─── Pager ────────────────────────────────────────────────────────────────────────────────
   Real links (?page=n) so paging works before the circuit is up and every page is shareable. */
.ptv-pager { display: flex; justify-content: center; align-items: center; gap: .375rem;
    flex-wrap: wrap; margin: 2.5rem 0 0; }
.ptv-pager__link { display: inline-grid; place-items: center; min-width: 40px; height: 40px;
    padding: 0 .75rem; border-radius: 999px; text-decoration: none;
    background: var(--tv-fill); border: 1px solid var(--tv-hairline); color: var(--ptv-muted);
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: .84rem; font-weight: 700; }
.ptv-pager__link:hover { border-color: var(--tv-hover-border); color: var(--ptv-text); }
.ptv-pager__link[aria-current="page"] { background: var(--ptv-primary); border-color: var(--ptv-primary); color: var(--tp-white); }
.ptv-pager__link[aria-disabled="true"] { opacity: .4; pointer-events: none; }
.ptv-pager__gap { color: var(--tv-text-faint); padding: 0 .25rem; }

/* ─── Category hero ─────────────────────────────────────────────────────────────────────── */
.ptv-cathero { position: relative; overflow: hidden; border-radius: 16px; min-height: 13rem;
    display: flex; flex-direction: column; justify-content: flex-end; padding: 1.75rem 2rem;
    background: var(--tv-art-placeholder); margin: .5rem 0 1.25rem;
    box-shadow: inset 4px 0 0 var(--cat-accent, var(--ptv-primary)); }
.ptv-cathero__art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .45; }
.ptv-cathero__scrim { position: absolute; inset: 0;
    background: linear-gradient(90deg, var(--tv-hero-scrim-ink) 0%, var(--tv-hero-scrim-mid) 55%, var(--tv-hero-scrim-glow) 100%); }
.ptv-cathero > *:not(img):not(.ptv-cathero__scrim) { position: relative; }
.ptv-cathero__eyebrow { font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11.5px;
    letter-spacing: .06em; text-transform: uppercase; color: var(--tv-text-dim); margin: 0 0 .375rem; }
.ptv-cathero__eyebrow a { color: inherit; }
.ptv-cathero__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif; font-size: 2.5rem;
    letter-spacing: -.01em; line-height: 1.05; margin: 0; }
.ptv-cathero__desc { color: var(--ptv-muted); max-width: 38rem; margin: .5rem 0 .75rem; font-size: .95rem; }
.ptv-cathero__stats { display: flex; gap: 1.25rem; flex-wrap: wrap; color: var(--tv-text-dim);
    font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 12.5px; font-weight: 500; }
.ptv-cathero__stats strong { color: var(--ptv-text); font-weight: 700; }

/* Sideways: the other categories as chips under the hero. Reuses .ptv-chip. */
.ptv-catchips { display: flex; gap: .5rem; flex-wrap: wrap; margin: 0 0 .25rem; }
.ptv-catchips .ptv-chip { text-decoration: none; display: inline-flex; align-items: center; gap: .375rem; }
.ptv-catchips .ptv-chip::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--cat-accent, var(--ptv-primary)); }

/* ─── Channel page: "Browse this channel" ───────────────────────────────────────────────── */
.ptv-chan__cats { margin: 0 0 1.5rem; }
.ptv-chan__cats-label { font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11.5px;
    letter-spacing: .04em; text-transform: uppercase; color: var(--tv-text-faint); margin: 0 0 .5rem; }

/* ─── Phone ─────────────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
    .ptv-explore__title { font-size: 1.75rem; }
    .ptv-cats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .5rem; }
    .ptv-cat { aspect-ratio: 16 / 10; }
    .ptv-cat__name { font-size: .95rem; margin: 0 .625rem; }
    .ptv-cat__count { margin: .125rem .625rem .5rem; }
    .ptv-filters > .ptv-filter, .ptv-filters > .ptv-filters__sort { display: none; }
    .ptv-filters__open { display: inline-flex; }
    .ptv-filters { justify-content: space-between; }
    .ptv-cathero { padding: 1.25rem; min-height: 11rem; }
    .ptv-cathero__title { font-size: 1.875rem; }
    .ptv-pager__link { min-width: 44px; height: 44px; }
    .ptv-pager__num--far { display: none; }
}

/* The filter sheet (phone). Full height, its own scroll, a sticky results button. */
.ptv-sheet { position: fixed; inset: 0; z-index: 60; display: flex; flex-direction: column;
    background: var(--tv-drawer); }
.ptv-sheet[hidden] { display: none; }
.ptv-sheet__hdr { display: flex; align-items: center; justify-content: space-between;
    padding: 1rem 1.25rem; border-bottom: 1px solid var(--tv-hairline); }
.ptv-sheet__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif; font-size: 1.125rem; margin: 0; }
.ptv-sheet__close { background: var(--tv-fill); border: 1px solid var(--tv-hairline); color: var(--ptv-text);
    border-radius: 999px; width: 40px; height: 40px; font-size: 16px; cursor: pointer; }
.ptv-sheet__body { flex: 1; overflow-y: auto; padding: .5rem 1.25rem 1.5rem; }
.ptv-sheet__group { padding: 1rem 0; border-bottom: 1px solid var(--tv-hairline); }
.ptv-sheet__label { font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 11.5px;
    letter-spacing: .04em; text-transform: uppercase; color: var(--tv-text-faint); margin: 0 0 .625rem; }
.ptv-sheet__chips { display: flex; gap: .5rem; flex-wrap: wrap; }
.ptv-sheet__chips .ptv-chip { min-height: 40px; }
.ptv-sheet__ftr { display: flex; gap: .75rem; padding: .875rem 1.25rem; border-top: 1px solid var(--tv-hairline); }
.ptv-sheet__ftr .ptv-btn--primary { flex: 1; justify-content: center; }

/* ─── Promotion slots (Featured Promotions) ─────────────────────────────────────────────────
   A slot follows every 8th video in a grid. A grid that carries slots uses fixed 4 / 2 / 1
   columns instead of auto-fill, so 8 videos are always whole rows and a full-width slot never
   leaves a hole in the row above it. Slots are always labelled "Sponsored" in words. */
.ptv-row__items--slotted { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1023px) { .ptv-row__items--slotted { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 559px)  { .ptv-row__items--slotted { grid-template-columns: minmax(0, 1fr); } }
.ptv-slot { grid-column: 1 / -1; list-style: none; margin: .5rem 0; }
.ptv-slot--between { margin: 0 0 2.5rem; }

.ptv-sponsored { font-family: var(--ptv-font-mono), ui-monospace, monospace; font-size: 10.5px;
    font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--tp-accent-gold); }

/* Leaderboard: Explore and On demand. One promotion at a time, rotating every 10 s. */
.ptv-promo { position: relative; display: grid; grid-template-columns: minmax(0, 38%) minmax(0, 1fr) auto;
    align-items: center; gap: 1.5rem; padding: 1rem 1.5rem 1rem 1rem;
    background: var(--tv-surface); border: 1px solid var(--tv-hairline); border-radius: 16px;
    box-shadow: 0 2px 12px var(--tv-shade-40); }
.ptv-promo__media { aspect-ratio: 16 / 7; border-radius: 12px; overflow: hidden; background: var(--tv-art-placeholder);
    box-shadow: inset 0 0 0 1px var(--tv-hairline); }
.ptv-promo__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ptv-promo--noimg { grid-template-columns: minmax(0, 1fr) auto; padding-left: 1.5rem;
    box-shadow: inset 4px 0 0 var(--tp-accent-gold), 0 2px 12px var(--tv-shade-40); }
.ptv-promo--noimg .ptv-promo__media { display: none; }
.ptv-promo__body { min-width: 0; }
.ptv-promo__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif; font-size: 1.375rem;
    line-height: 1.2; margin: .375rem 0 .25rem; color: var(--ptv-text); }
.ptv-promo__desc { color: var(--ptv-muted); font-size: .9rem; margin: 0; }
.ptv-promo__acts { display: flex; flex-direction: column; align-items: flex-end; gap: .75rem; }
.ptv-promo__dots { display: flex; gap: 2px; }
.ptv-promo__dot { width: 24px; height: 24px; padding: 0; border: 0; background: none; cursor: pointer;
    display: grid; place-items: center; }
.ptv-promo__dot::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--tv-fill-border); transition: background .2s ease, width .2s ease; }
.ptv-promo__dot[aria-current="true"]::before { width: 18px; border-radius: 999px; background: var(--tp-accent-gold); }
@media (max-width: 640px) {
    .ptv-promo, .ptv-promo--noimg { grid-template-columns: minmax(0, 1fr); gap: .875rem; padding: .875rem; }
    .ptv-promo__acts { flex-direction: row; justify-content: space-between; align-items: center; }
    .ptv-promo__title { font-size: 1.125rem; }
}

/* Small carousel: the home page. Three compact tiles in view (two on a tablet, one and a peek on a
   phone), scroll-snapped; arrows move one tile; it advances every 10 s and stops on hover, on
   focus and under reduced motion. The ONLY horizontal carousel on PulseTV: video rows stay grids. */
.ptv-promo-carousel { position: relative; }
.ptv-promo-carousel__head { display: flex; align-items: center; gap: .75rem; margin: 0 0 .625rem; }
.ptv-promo-carousel__nav { margin-left: auto; display: flex; gap: .375rem; }
.ptv-promo-carousel__btn { width: 36px; height: 36px; border-radius: 50%; cursor: pointer;
    background: var(--tv-fill); border: 1px solid var(--tv-hairline); color: var(--ptv-text); font-size: 15px; }
.ptv-promo-carousel__btn:hover { border-color: var(--tv-hover-border); }
.ptv-promo-carousel__track { display: grid; grid-auto-flow: column; grid-auto-columns: calc((100% - 2rem) / 3);
    gap: 1rem; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
    scrollbar-width: none; list-style: none; padding: 0; margin: 0; }
.ptv-promo-carousel__track::-webkit-scrollbar { display: none; }
@media (max-width: 1023px) { .ptv-promo-carousel__track { grid-auto-columns: calc((100% - 1rem) / 2); } }
@media (max-width: 559px)  { .ptv-promo-carousel__track { grid-auto-columns: 86%; } .ptv-promo-carousel__nav { display: none; } }
@media (prefers-reduced-motion: reduce) { .ptv-promo-carousel__track { scroll-behavior: auto; } }
.ptv-promo-card { scroll-snap-align: start; position: relative; display: flex; align-items: flex-end;
    aspect-ratio: 16 / 6.5; border-radius: 12px; overflow: hidden; text-decoration: none; color: var(--ptv-text);
    background: var(--tv-art-placeholder); box-shadow: inset 0 0 0 1px var(--tv-hairline); }
.ptv-promo-card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .75; }
.ptv-promo-card__scrim { position: absolute; inset: 0; background: linear-gradient(to top, var(--tv-tile-scrim) 15%, var(--tv-tile-scrim-fade) 100%); }
.ptv-promo-card__body { position: relative; display: flex; align-items: flex-end; justify-content: space-between;
    gap: .75rem; width: 100%; padding: .75rem .875rem; }
.ptv-promo-card__title { font-family: var(--ptv-font-display), 'DM Sans', sans-serif; font-size: 1rem; font-weight: 700; line-height: 1.2; margin: 0; }
.ptv-promo-card__desc { color: var(--tv-text-dim); font-size: 12px; margin: 2px 0 0; }
.ptv-promo-card__cta { flex: none; font-size: 12px; font-weight: 700; padding: 6px 12px; border-radius: 999px;
    background: var(--ptv-primary); color: var(--tp-white); }
.ptv-promo-card:hover { box-shadow: inset 0 0 0 1px var(--tv-hover-border), 0 10px 34px var(--tv-card-glow); }
.ptv-promo-card:focus-visible { outline: 3px solid var(--ptv-focus); outline-offset: 3px; }
.ptv-promo-carousel__dots { display: flex; justify-content: center; gap: 2px; margin-top: .375rem; }
.ptv-slot-row { list-style: none; padding: 0; margin: 0; }
/* Home: the fixed sponsored section between the hero and "Top on the island this week". */
.ptv-sponsored-top { margin-block: 0 2rem; }

/* "This show may include adverts" — told before the viewer buys (Antigua CPA 2025 s.123,
   s.76(2)(b)). Words carry it; the glyph is decoration. Dim text on the dark shell, the card's
   own ink on the light unlock card and purchase summary. */
.ptv-adnotice { display: flex; align-items: flex-start; gap: .5rem; margin: .75rem 0 0;
    font-size: 13.5px; line-height: 1.5; color: var(--tv-text-dim); max-width: 660px; }
.ptv-adnotice__glyph { flex: none; width: 1.05em; height: 1.05em; margin-top: .2em; }
.ptv-gate .ptv-adnotice { color: var(--tp-text); justify-content: center; text-align: left;
    margin: 0 auto 1rem; max-width: 26rem; }
.ptv-purchase__card .ptv-adnotice { color: var(--tp-text); margin: 0 0 .75rem; }
