/* 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)); }
/* Catalogue rows are horizontal shelves, per the prototype: fixed-width tiles that scroll,
   not a wrapping grid. The channel lists keep the grid. */
.ptv-row__items--shelf { display: flex; overflow-x: auto; padding: .375rem .125rem 1.125rem;
                         scroll-snap-type: x proximity; }
.ptv-row__items--shelf > li { flex: none; width: 300px; scroll-snap-align: start; }
.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; }
.ptv-top__items { list-style: none; display: flex; gap: .375rem; overflow-x: auto;
                  padding: .625rem .125rem 1.125rem; margin: 0; }
.ptv-top__item { flex: none; }
.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; width: 250px; 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, #241640); box-shadow: 0 2px 12px rgba(0, 0, 0, .4); }
.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: linear-gradient(135deg, #3a2263 0%, #241543 55%, #2f1a52 100%);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .09); }

/* 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 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); }

.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); }
.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. */
.ptv-player { position: relative; border-radius: 16px; overflow: hidden; background: var(--tp-ink-true);
              box-shadow: 0 20px 60px var(--tv-tile-shadow); }

/* 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); }
.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; }
.ptv-player__frame { aspect-ratio: 16/9; width: 100%; }
.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); }
/* 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%); } }

.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; }

.ptv-row__sub { color: var(--ptv-muted); font-size: .82rem; margin: -.5rem 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. */
.ptv-holdcard { position: absolute; inset: 0; z-index: 5; 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); }
.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__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 { margin-top: 7px; }
.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); }
.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. */
.ptv-player__unlockover { position: absolute; inset: 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; }

/* 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; } }
