/*
 * RASTATIME 2026-05-22 — Now-Playing Show / Track Widget styling.
 *
 * Minimal text-only widget (no boxes, no shadows, no decoration).
 * Two themes that the operator picks via the ?theme= URL param:
 *
 *   .np-theme-auto  — follow the host browser's prefers-color-scheme
 *   .np-theme-dark  — light text on transparent background
 *   .np-theme-light — dark text on transparent background
 *
 * Background is intentionally transparent so the widget blends into
 * whatever the embedding page's layout looks like.  The iframe host
 * sets its own background colour.
 */

html, body {
    margin: 0;
    padding: 0;
    background: transparent;
}

.np-widget {
    /*  System-font stack keeps the widget visually native to whichever
        page embeds it, instead of forcing an unwanted typography.  */
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
                 Roboto, Helvetica, Arial, sans-serif;
    font-size: 14px;
    line-height: 1.35;
    padding: 4px 8px;
    box-sizing: border-box;

    /*  RASTATIME 2026-05-22 — width cap.  Operator request: a
        narrow widget reads more cleanly inside the sidebar of a
        public site than a full-width strip.  The 300 px default
        applies to the widget body; widget-family overrides below
        (np-now-playing, np-on-air-today) bump specific widgets
        to 340 px on operator request 2026-05-22 v5.  The iframe
        itself can still be sized larger by the embedding page
        (the iframe will have white space around the widget body).
        Set the iframe width to match for a flush fit.  */
    max-width: 300px;
    margin: 0;

    /*  Default theme variables; overridden below by the explicit
        theme classes.  --np-fg is the only thing that actually
        changes; everything else inherits transparency.  */
    --np-fg: #222;
    --np-fg-muted: #666;
    color: var(--np-fg);
}

/* ───── per-widget-family width overrides ─────
 *
 *  RASTATIME 2026-05-22 v5 — Now-Playing (show + track) and
 *  On-Air-Today widgets get bumped to 340 px on operator request
 *  ("show / track / today look better with a bit more breathing
 *  room").  Next/Latest Shows stays at the 300 px default — it
 *  serves a different sidebar use-case where the narrower body
 *  reads more cleanly.
 */
.np-widget.np-now-playing,
.np-widget.np-on-air-today {
    max-width: 340px;
}

/* ───── theme switching ───── */

.np-widget.np-theme-light {
    --np-fg: #1a1a1a;
    --np-fg-muted: #555;
}

.np-widget.np-theme-dark {
    --np-fg: #f0f0f0;
    --np-fg-muted: #bbb;
}

/*
 * Auto mode: respect the browser's preference.  Default to light
 * because more embedding pages are light-themed than dark.
 */
.np-widget.np-theme-auto {
    --np-fg: #1a1a1a;
    --np-fg-muted: #555;
}
@media (prefers-color-scheme: dark) {
    .np-widget.np-theme-auto {
        --np-fg: #f0f0f0;
        --np-fg-muted: #bbb;
    }
}

/* ───── two-line layout ───── */

.np-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1em;
    /* Truncate the name when the iframe is narrow so the time on the
       right always stays visible (the time is the more time-critical
       half of the row). */
    overflow: hidden;
}

.np-row-current {
    font-weight: 600;
}

.np-row-next {
    color: var(--np-fg-muted);
    font-size: 0.92em;
    margin-top: 2px;
}

.np-name {
    /*  Take all the available width; ellipsis when overflowing.
        Operators who prefer scrolling text over truncation can
        add the `overflow=marquee` URL parameter (the widget
        template hangs the `np-overflow-marquee` class on the
        body in that case).  */
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ───── marquee mode (?overflow=marquee) ───── */

/*  CSS-only marquee implementation: the inner text container
    slides left from 0 to its negative width over a duration that
    scales with text length so longer text doesn't scroll
    insanely fast.  Pure CSS keeps the JS-side dumb and means the
    scroll continues even if the JS poller hangs.  */
.np-widget.np-overflow-marquee .np-name {
    overflow: hidden;
    text-overflow: clip;
    position: relative;
    /*  The marquee track is rendered inside the same .np-name
        span; we hand the actual scrolling job to a CSS-only
        animation that only kicks in when the text genuinely
        overflows.  Pseudo-element approach so we don't need
        wrapper markup.  */
}

.np-widget.np-overflow-marquee .np-name {
    display: inline-block;
    /*  Allow the inner text to be wider than its container so
        the animation has something to translate against.  */
    white-space: nowrap;
}

@keyframes np-marquee-scroll {
    /*  Hold the start for 1.5 s so the operator reads the
        beginning before the scroll starts; then translate left
        until the text is fully off-screen; then snap back.
        The translate distance reads a CSS variable so the same
        keyframes work for both the 300 px shows-list widgets
        and the 340 px now-playing / on-air-today widgets — see
        --np-marquee-end below.  */
    0%   { transform: translateX(0); }
    15%  { transform: translateX(0); }
    85%  { transform: translateX(calc(-100% + var(--np-marquee-end, 280px))); }
    100% { transform: translateX(calc(-100% + var(--np-marquee-end, 280px))); }
}

/*  Wider widgets get a larger marquee end-offset so the scroll
    matches the container width.  Numbers track the body padding
    (~10 px each side) subtracted from the body width.  */
.np-widget.np-now-playing,
.np-widget.np-on-air-today {
    --np-marquee-end: 320px;
}

.np-widget.np-overflow-marquee .np-row-current .np-name,
.np-widget.np-overflow-marquee .np-row-next .np-name {
    /*  Animation only runs when the text actually overflows —
        a clean way to detect that is to compare scrollWidth vs
        clientWidth in JS, but for a CSS-only approach we just
        kick the animation off for everything and let short text
        sit visually at translateX(0) for the whole loop (the
        end-state translate is clamped to a tiny negative
        value).  The 24s duration is calibrated for typical
        Artist - Title strings in the 30–60 char range.  */
    animation: np-marquee-scroll 24s ease-in-out infinite;
}

.np-time {
    /*  Fixed-width-ish; right-aligned, doesn't shrink.  */
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ───── overflow / error states (set by JS) ───── */

.np-widget.np-stale .np-time {
    /* When the JS-poll fails repeatedly the data goes stale;
       muted colour signals "this might not be accurate". */
    opacity: 0.5;
}

.np-widget.np-empty .np-name {
    /* When there is no current show or track, the placeholder
       text shows in muted style. */
    color: var(--np-fg-muted);
    font-style: italic;
}

/* ─────────────────────────────────────────────────────────────
 *  RASTATIME 2026-05-22 — Next-Shows / Latest-Shows widget.
 *
 *  Shares the .np-widget base above (theme variables, transparent
 *  background, system-font stack, 300 px width cap) and adds a
 *  small chronological list under the show title.
 *
 *  Layout:
 *    Show Name                                (.np-row-title)
 *    Tue 21 May    18:00 - 19:00              (.np-list-row, .np-list-date+.np-list-time)
 *    Tue 28 May    18:00 - 19:00
 *    ...
 * ───────────────────────────────────────────────────────────── */

.np-widget.np-shows-list .np-row-title {
    /*  Match the two-line widget's `np-row-current` styling exactly
        so the Now-Playing and Shows-List widgets read as a visual
        family.  Color is the full --np-fg (not muted) so the theme
        switch is clearly visible — operator request 2026-05-22 v2.  */
    color: var(--np-fg);
    font-weight: 600;
    margin-bottom: 4px;
    display: flex;
    align-items: baseline;
    overflow: hidden;
}

.np-widget.np-shows-list .np-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.np-widget.np-shows-list .np-list-row {
    /*  Listing rows mirror the "current row" treatment of the
        Now-Playing widget: full --np-fg colour (not muted), so the
        theme switch flips text from near-white (dark) to near-
        black (light) instead of grey-to-grey.  Previously all rows
        used --np-fg-muted which made the three themes look almost
        identical on most backdrops.  */
    color: var(--np-fg);
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1em;
    font-size: 0.92em;
    line-height: 1.45;
    overflow: hidden;
}

.np-widget.np-shows-list .np-list-date {
    /*  Date column: left-aligned, takes whatever width it needs;
        the time-of-day cell pins itself to the right.  */
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.np-widget.np-shows-list .np-list-time {
    /*  Time-of-day uses the full --np-fg colour so it matches the
        treatment in the On-Air-Today widget (operator request
        2026-05-22 v3: "Light-Theme Farben angleichen") — without
        this, the time cell read as a mid-grey on the light theme
        that was harder to scan than the date cell next to it.  */
    color: var(--np-fg);
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.np-widget.np-shows-list .np-list-empty {
    /*  Placeholder row shown when the requested show has no
        instances in the chosen direction, or when the iframe URL
        is missing show_id / show.  Muted-italic treatment matches
        the two-line widget's empty-state cell.  */
    color: var(--np-fg-muted);
    font-style: italic;
}

/* ─────────────────────────────────────────────────────────────
 *  RASTATIME 2026-05-22 — On-Air-Today widget.
 *
 *  Layout mirrors the operator's screenshot:
 *
 *     ┌────────────────────────────┐
 *     │ On Air Today           ▔   │  ← accent-tinted header
 *     ├────────────────────────────┤
 *     │ 16:00 -      Newslink      │
 *     │ 16:30                      │  ← time-of-day left, name right
 *     │ 16:30 -      Pages of His… │     current row in bold
 *     │ 17:00                      │
 *     │ ...                        │
 *     └────────────────────────────┘
 *
 *  Shares the .np-widget base above (theme variables, transparent
 *  background, system-font stack, 300 px width cap).
 * ───────────────────────────────────────────────────────────── */

.np-widget.np-on-air-today {
    /*  Generous line-height so the rows have visual breathing
        room — operator request 2026-05-22 v4 (row height roughly
        doubled).  */
    line-height: 1.6;
    font-size: 1.1em;
    font-weight: 700;
}

.np-widget.np-on-air-today .np-header {
    /*  Accent band at the top — uses the muted variable for the
        underline so it blends with whichever theme the embedding
        page uses.  Heavier weight + bigger size align with the
        operator-requested "all bold, all bigger" treatment.  */
    color: var(--np-fg);
    font-weight: 700;
    font-size: 1.2em;
    padding-bottom: 6px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--np-fg-muted);
}

.np-widget.np-on-air-today .np-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.np-widget.np-on-air-today .np-on-air-row {
    /*  Each row: time-of-day on the left, show name on the right.
        Uses the same flex skeleton as .np-list-row in the shows-
        list widget so the marquee / ellipsis overflow handling
        works identically.

        Padding 12 px 0 doubles the row height vs the v1 layout
        (operator request 2026-05-22 v4).  Weight inherits the
        700 set on the parent .np-on-air-today, so every row reads
        as bold — the operator explicitly asked for this so the
        widget is legible at sidebar embed sizes without needing
        to zoom.  */
    color: var(--np-fg);
    display: flex;
    align-items: baseline;
    gap: 0.8em;
    padding: 12px 0;
    border-top: 1px dotted var(--np-fg-muted);
    overflow: hidden;
}

.np-widget.np-on-air-today .np-on-air-row:first-child {
    /*  No top-border on the first row — the header already
        provides the dividing line above it.  Padding-top kept
        at 12 px so the spacing between header and first row
        matches the inter-row spacing.  */
    border-top: 0;
}

.np-widget.np-on-air-today .np-on-air-row.np-row-current {
    /*  Operator request 2026-05-22 v4: with every row already
        bold, the "current row" no longer needs an extra weight
        flourish — it would just collide with the new uniform
        700 weight.  Kept the selector live (empty) so we have
        a clean attachment point if the operator later wants
        the current row distinguished by colour or background.  */
}

.np-widget.np-on-air-today .np-on-air-time {
    /*  Time-of-day cell: fixed width-ish, left-aligned, monospace-
        ish digits via tabular-nums so "16:00 - 16:30" doesn't
        wobble visually between rows.  */
    flex: 0 0 auto;
    min-width: 6.5em;
    color: var(--np-fg);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.np-widget.np-on-air-today .np-on-air-name {
    /*  Show-name cell: takes the remaining width.  When the name
        overflows the 300 px widget body the marquee / ellipsis
        rules on the base .np-name selector handle it.  */
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.np-widget.np-on-air-today .np-on-air-name a {
    /*  Links inherit the row's text colour so the link rendering
        feels native to the widget's theme rather than a fixed
        bright blue that fights with the embedding page.  Subtle
        underline keeps the affordance — operators who want a
        clean text-only look pass ?link=plain on the embed URL
        to disable the underline (see .np-link-plain below).  */
    color: inherit;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

.np-widget.np-on-air-today .np-on-air-name a:hover {
    /*  Pop the underline on hover so the operator gets a clear
        cue that the name is clickable.  */
    text-decoration-thickness: 2px;
}

/*  ?link=plain: drop the underline entirely (both default and
    hover state) so the show names read as part of the list
    rather than as obvious navigation.  Cursor:pointer is kept
    so the affordance survives via the pointer change.  */
.np-widget.np-on-air-today.np-link-plain .np-on-air-name a,
.np-widget.np-on-air-today.np-link-plain .np-on-air-name a:hover {
    text-decoration: none;
}

.np-widget.np-on-air-today .np-list-empty {
    /*  Placeholder row when the day has no remaining shows (or
        when looking at a future day that hasn't been scheduled
        yet).  Muted-italic, matches the other widgets.  */
    color: var(--np-fg-muted);
    font-style: italic;
    padding: 6px 0;
}

/* ─────────────────────────────────────────────────────────────
 *  RASTATIME 2026-05-22 — Weekly Schedule widget v2.
 *
 *  Modernised replacement for the upstream jQuery + Handlebars
 *  template.  Inherits the .np-widget base (theme variables,
 *  transparent background, system-font stack, width cap) and adds
 *  a header with ◀ / ▶ week-navigation buttons, a horizontal day-
 *  tab strip, and a per-day show list.
 *
 *  Layout:
 *
 *     ┌─────────────────────────────────────────┐
 *     │ ◀  21 May – 27 May 2026          ▶      │  ← header band
 *     ├─────────────────────────────────────────┤
 *     │ Mon  Tue  Wed  Thu  Fri  Sat  Sun       │  ← day tabs
 *     ├─────────────────────────────────────────┤
 *     │ 06:00 – 08:00   Morning Show            │
 *     │ 08:00 – 10:00   Newslink                │
 *     │ ...                                     │
 *     └─────────────────────────────────────────┘
 * ───────────────────────────────────────────────────────────── */

.np-widget.np-weekly-schedule {
    /*  Weekly schedule is the only widget that consumes vertical
        space proportional to the number of shows per day, so we
        give it a more generous max-width than the other widgets
        — 400 px reads cleanly without making the rows feel
        cramped, and still fits common sidebar layouts.

        Operator request 2026-05-22 v6: visual styling pulls
        from the legacy weekly-schedule-widget look — rectangular
        (no border-radius), accent colour #459B8F (LibreTime
        teal) for today / hover / active states.  Exposed as a
        --np-accent variable so a future style override could
        switch it to a station-specific brand colour without
        rewriting the rules.  */
    /* RASTATIME 2026-09-29: the UI primary colour (Settings -> General),
       was LibreTime teal #459B8F */
    --np-accent: var(--rt-accent, #ff5d1a);
    --np-accent-hover: rgba(var(--rt-accent-rgb, 255, 93, 26), 0.4);

    /*  RASTATIME 2026-05-25 — operator-requested size bump from
        400 px to 670 px container width.  All other dimensions
        below scaled proportionally (≈ 1.67×) — fonts via the
        font-size bump here (1em → 1.2em) so em-based padding /
        gap values inherit the new scale, plus targeted bumps on
        px-based padding / heights / min-widths.  */
    max-width: 670px;
    line-height: 1.5;
    font-size: 1.2em;

    /*  Optional background colour + image, controlled by
        --np-bg-color (?bgcolor=#hex) and --np-bg-image (?bg=URL).
        Both default to transparent / none so the body blends
        into the embedding page unless the operator opts in.
        When both are set, the image renders over the colour
        (typical use: image with transparent regions + a fallback
        colour while the image loads).  */
    background-color: var(--np-bg-color, transparent);
    background-image: var(--np-bg-image, none);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
}

.np-widget.np-weekly-schedule .np-ws-header {
    /*  Top band: ◀ button, week-range label, ▶ button on the
        right.  Flex with space-between keeps the buttons pinned
        to the edges and the label centred regardless of week
        length (some January / December windows span the
        year boundary, which makes the label string longer).  */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5em;
    padding-bottom: 10px;
    margin-bottom: 10px;
    border-bottom: 1px solid var(--np-fg-muted);
    color: var(--np-fg);
    font-weight: 700;
    font-size: 1em;
}

.np-widget.np-weekly-schedule .np-ws-nav {
    /*  Square-ish nav buttons — no border-radius (kantig look,
        operator request 2026-05-22 v6).  Hover state lights up
        the accent colour at low alpha so the button reads as
        interactive without dominating the header.  */
    background: transparent;
    border: 0;
    padding: 4px 14px;
    color: var(--np-fg);
    cursor: pointer;
    font-size: 1.3em;
    line-height: 1;
    flex: 0 0 auto;
}

.np-widget.np-weekly-schedule .np-ws-nav:hover:not(:disabled) {
    background: var(--np-accent-hover);
}

.np-widget.np-weekly-schedule .np-ws-nav:disabled {
    /*  When the operator has navigated to the boundary (-2 weeks
        back or +4 weeks ahead), the matching arrow disables so
        the user sees they've hit the limit.  */
    opacity: 0.3;
    cursor: not-allowed;
}

.np-widget.np-weekly-schedule .np-ws-range {
    /*  Centred date-range label, e.g. "21 May – 27 May 2026".  */
    flex: 1 1 auto;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.np-widget.np-weekly-schedule .np-ws-tabs {
    /*  Horizontal strip of 7 day-of-week tabs.  list-style off,
        gap controlled by flex; each tab is a vertical mini-stack
        of "Mon" + "12" — same convention as the original widget
        so existing operators recognise the layout.  */
    display: flex;
    list-style: none;
    margin: 0 0 10px 0;
    padding: 0;
    gap: 0.3em;
}

.np-widget.np-weekly-schedule .np-ws-tab {
    /*  Square tabs, no border-radius (kantig look).  Hover
        background uses the accent at low alpha — matches the
        legacy widget's "lift on hover" feel without the chunky
        teal block.  */
    flex: 1 1 0;
    text-align: center;
    padding: 8px 0;
    cursor: pointer;
    color: var(--np-fg-muted);
    border-bottom: 3px solid transparent;
    font-weight: 700;
    line-height: 1.2;
}

.np-widget.np-weekly-schedule .np-ws-tab:hover {
    /*  Operator request 2026-05-22 v6: hover state uses the
        accent at low alpha — feels lit-up without overwhelming
        the rest of the row.  */
    background: var(--np-accent-hover);
    color: var(--np-fg);
}

.np-widget.np-weekly-schedule .np-ws-tab .np-ws-tab-dow {
    /*  Mon / Tue / Wed label — small, slightly smaller than the
        day number below so the day-of-month reads as the primary
        identifier.  */
    display: block;
    font-size: 0.85em;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.np-widget.np-weekly-schedule .np-ws-tab .np-ws-tab-dom {
    /*  Day-of-month number — primary visual element of the tab.  */
    display: block;
    font-size: 1.15em;
    margin-top: 2px;
}

.np-widget.np-weekly-schedule .np-ws-tab.np-ws-tab-active {
    /*  Active tab: underline accent at the bottom in the full
        accent colour — clear which day's panel is showing.  */
    color: var(--np-fg);
    border-bottom-color: var(--np-accent);
}

.np-widget.np-weekly-schedule .np-ws-tab.np-ws-tab-today {
    /*  Today indicator: solid accent background (operator
        request 2026-05-22 v6).  No border-radius keeps the
        kantig look.  Wins visually even when the active-tab
        underline is on a different day.  */
    background: var(--np-accent);
    color: var(--rt-accent-fg, #fff);
}

.np-widget.np-weekly-schedule .np-ws-tab.np-ws-tab-today:hover {
    /*  Hover on today: stay solid accent (don't fade to the
        hover-alpha variant) since today is already the
        accent state — fading would feel like losing the
        highlight.  */
    background: var(--np-accent);
    color: var(--rt-accent-fg, #fff);
}

.np-widget.np-weekly-schedule .np-ws-content {
    /*  Container for the 7 day-panels — only one is visible at a
        time (toggled via .np-ws-day-active by the JS).  Min-
        height keeps the iframe from jumping when switching
        between a day with many shows and one with none.  */
    min-height: 200px;
}

.np-widget.np-weekly-schedule .np-ws-day {
    /*  Each day-panel is its own scroll container (operator
        request 2026-05-22 v7).  Max-height keeps the panel
        inside the iframe height; if the day has more shows
        than fit, the visitor scrolls within the panel rather
        than the iframe (which has scrolling="no").  When the
        operator activates the today-tab, JS scrolls the
        currently-airing show into the panel viewport so the
        user lands directly on "what's on right now" without
        having to scroll past the early-morning programme.

        Sized for the 670 px square iframe (operator wanted the
        widget square 2026-05-25, height matches the new
        670 px width).  670 - header (~56) - tab strip (~84) -
        bottom margin (~10) ≈ 520 px of usable panel height.  */
    display: none;
    max-height: 520px;
    overflow-y: auto;
    /*  Subtle scrollbar that matches the muted theme variable,
        only visible while scrolling (webkit) or always (firefox
        — falls back to default).  */
    scrollbar-width: thin;
    scrollbar-color: var(--np-fg-muted) transparent;
}

.np-widget.np-weekly-schedule .np-ws-day::-webkit-scrollbar {
    width: 8px;
}

.np-widget.np-weekly-schedule .np-ws-day::-webkit-scrollbar-thumb {
    background: var(--np-fg-muted);
}

.np-widget.np-weekly-schedule .np-ws-day.np-ws-day-active {
    display: block;
}

.np-widget.np-weekly-schedule .np-ws-row {
    /*  One row per show: HH:MM-HH:MM on the left, show name on
        the right.  Same flex skeleton as the other list-shaped
        widgets so the marquee / ellipsis overflow rules apply.

        Transparent left-border on every row so the layout
        doesn't shift when the .np-ws-row-current rule replaces
        it with an accent stripe.  */
    color: var(--np-fg);
    display: flex;
    align-items: baseline;
    gap: 1em;
    padding: 10px 0 10px 12px;
    border-top: 1px dotted var(--np-fg-muted);
    border-left: 4px solid transparent;
    overflow: hidden;
}

.np-widget.np-weekly-schedule .np-ws-row:first-child {
    border-top: 0;
}

.np-widget.np-weekly-schedule .np-ws-row-current {
    /*  Accent left-border so the operator sees which show is
        airing right now after the auto-scroll lands.  Subtle on
        purpose — the row stays in the visual rhythm of the
        list rather than dominating it.  */
    border-left-color: var(--np-accent);
}

.np-widget.np-weekly-schedule .np-ws-row-time {
    flex: 0 0 auto;
    min-width: 6.5em;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    font-weight: 700;
}

.np-widget.np-weekly-schedule .np-ws-row-name {
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 700;
}

.np-widget.np-weekly-schedule .np-ws-row-name a {
    /*  Same link treatment as the On-Air-Today widget: inherit
        the row colour, underline only when ?link=underline.  */
    color: inherit;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}

.np-widget.np-weekly-schedule .np-ws-row-name a:hover {
    /*  Hover on a show-name link uses the accent colour so the
        operator gets a clear interaction cue.  Works for both
        the underline and the plain link variant.  */
    color: var(--np-accent);
}

.np-widget.np-weekly-schedule.np-link-plain .np-ws-row-name a,
.np-widget.np-weekly-schedule.np-link-plain .np-ws-row-name a:hover {
    text-decoration: none;
}

.np-widget.np-weekly-schedule .np-ws-empty {
    color: var(--np-fg-muted);
    font-style: italic;
    padding: 12px 0;
    text-align: center;
}

/* ─────────────────────────────────────────────────────────────
 *  RASTATIME 2026-05-22 — Multi-View Schedule widget.
 *
 *  FullCalendar 6.1.10 (locally hosted) renders the time-grid.
 *  This block adds the surrounding chrome (header band, view
 *  switcher, modal) plus theme integration overrides so the
 *  FullCalendar default styling doesn't fight the Wave-1 look.
 *
 *  Kantig (no border-radius) throughout — matches the Weekly-
 *  Schedule widget's operator-requested square aesthetic.
 * ───────────────────────────────────────────────────────────── */

.np-widget.np-multi-view-schedule {
    /* RASTATIME 2026-09-29: the UI primary colour (Settings -> General),
       was LibreTime teal #459B8F */
    --np-accent: var(--rt-accent, #ff5d1a);
    --np-accent-hover: rgba(var(--rt-accent-rgb, 255, 93, 26), 0.4);

    /*  Multi-view fills its iframe — no inherent max-width cap
        like the other widgets, because the calendar can stretch
        usefully from sidebar widths (~400 px) to full-page
        embeds.  Operator controls the size via iframe width.  */
    max-width: 100%;
    line-height: 1.4;
    font-size: 0.9em;

    /*  Same --np-bg-color / --np-bg-image contract as the
        Weekly-Schedule widget.  */
    background-color: var(--np-bg-color, transparent);
    background-image: var(--np-bg-image, none);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;

    /*  Use the full iframe viewport — FullCalendar's
        `height: 'auto'` would otherwise grow indefinitely.  */
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* ─── Header band (nav + range label + view switcher) ─── */

.np-widget.np-multi-view-schedule .np-mv-header {
    display: flex;
    align-items: center;
    gap: 0.4em;
    padding: 6px 8px;
    border-bottom: 1px solid var(--np-fg-muted);
    color: var(--np-fg);
    font-weight: 600;
    flex-shrink: 0;
}

.np-widget.np-multi-view-schedule .np-mv-nav,
.np-widget.np-multi-view-schedule .np-mv-today,
.np-widget.np-multi-view-schedule .np-mv-view {
    background: transparent;
    border: 1px solid transparent;
    color: var(--np-fg);
    padding: 4px 10px;
    font: inherit;
    cursor: pointer;
    line-height: 1;
}

.np-widget.np-multi-view-schedule .np-mv-nav {
    padding: 4px 8px;
    font-size: 0.95em;
}

.np-widget.np-multi-view-schedule .np-mv-nav:hover,
.np-widget.np-multi-view-schedule .np-mv-today:hover,
.np-widget.np-multi-view-schedule .np-mv-view:hover {
    background: var(--np-accent-hover);
}

.np-widget.np-multi-view-schedule .np-mv-range {
    flex: 1 1 auto;
    text-align: center;
    font-variant-numeric: tabular-nums;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.np-widget.np-multi-view-schedule .np-mv-today {
    border-color: var(--np-fg-muted);
    font-size: 0.85em;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.np-widget.np-multi-view-schedule .np-mv-views {
    display: inline-flex;
    border: 1px solid var(--np-fg-muted);
    flex-shrink: 0;
}

.np-widget.np-multi-view-schedule .np-mv-view {
    border-left: 1px solid var(--np-fg-muted);
    font-size: 0.85em;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 4px 8px;
}

.np-widget.np-multi-view-schedule .np-mv-view:first-child {
    border-left: 0;
}

.np-widget.np-multi-view-schedule .np-mv-view.np-mv-view-active {
    background: var(--np-accent);
    color: var(--rt-accent-fg, #fff);
}

/* ─── Calendar mount ─── */

.np-widget.np-multi-view-schedule .np-mv-calendar {
    flex: 1 1 auto;
    overflow-y: auto;
    overflow-x: hidden;
    min-height: 0;
}

/* FullCalendar theme integration — colour overrides so the
   calendar inherits the Wave-1 theme variables rather than the
   FullCalendar defaults (which assume a white page chrome).
   Kantig: zero border-radius on every visible element so the
   look matches the Weekly-Schedule widget. */

.np-widget.np-multi-view-schedule .fc {
    color: var(--np-fg);
    font-family: inherit;
    --fc-page-bg-color: transparent;
    --fc-border-color: var(--np-fg-muted);
    --fc-now-indicator-color: var(--np-accent);
    --fc-today-bg-color: rgba(var(--rt-accent-rgb, 255, 93, 26), 0.08);
    --fc-neutral-bg-color: transparent;
    --fc-list-event-hover-bg-color: var(--np-accent-hover);
}

.np-widget.np-multi-view-schedule .fc table,
.np-widget.np-multi-view-schedule .fc th,
.np-widget.np-multi-view-schedule .fc td {
    border-color: var(--np-fg-muted);
}

.np-widget.np-multi-view-schedule .fc-col-header-cell {
    padding: 4px 0;
    font-weight: 700;
}

.np-widget.np-multi-view-schedule .fc-col-header-cell-cushion {
    color: var(--np-fg);
    text-decoration: none;
}

.np-widget.np-multi-view-schedule .fc-timegrid-slot-label-cushion {
    color: var(--np-fg-muted);
    font-variant-numeric: tabular-nums;
    font-size: 0.85em;
}

.np-widget.np-multi-view-schedule .fc-event {
    border-radius: 0;          /* kantig */
    /*  Operator request 2026-05-22 v10: zero vertical padding so
        even very short events (sub-15-min) have room for the
        time + name in one line.  Horizontal padding keeps the
        text from kissing the left/right borders.  */
    padding: 0 4px;
    cursor: pointer;
    border-width: 1px;
    border-style: solid;
    /*  Default colour when cc_show.background_color is empty —
        accent at low alpha so empty-coloured shows still read
        as scheduled blocks rather than invisible gaps.  */
    background: var(--np-accent-hover);
    border-color: var(--np-accent);
    color: var(--np-fg);
}

/*  Operator request 2026-05-22 v11: force every time-grid event
    to render in the FULL column width.  FullCalendar 6 packs
    back-to-back events (where end-time == next start-time) into
    half-width columns even with slotEventOverlap: false because
    it counts touch-boundaries as "shared columns".  In a radio
    schedule we never have truly overlapping shows, so a CSS
    override that pins every event harness to inset:0 is safe.
    !important is required because FullCalendar writes its own
    inline `left: x%; right: y%;` style on every render.

    v12 cleanup: drop `width: 100%` and `margin-right: 0` from
    the override — both broke FullCalendar's internal layout
    and made the events disappear entirely.  inset-inline-{
    start,end} alone is enough to expand each event to the
    full column width.  */
.np-widget.np-multi-view-schedule .fc-timegrid-event-harness {
    inset-inline-start: 0 !important;
    inset-inline-end: 0 !important;
}

.np-widget.np-multi-view-schedule .fc-event-title,
.np-widget.np-multi-view-schedule .fc-event-time {
    font-weight: 600;
}

.np-widget.np-multi-view-schedule .fc-event.np-mv-event-current {
    /*  Currently-airing event: subtle accent ring so the
        operator sees it after the auto-scroll lands.  Doesn't
        recolour the background — the show's own colour wins.  */
    outline: 2px solid var(--np-accent);
    outline-offset: -2px;
}

.np-widget.np-multi-view-schedule .fc-timegrid-now-indicator-line {
    border-color: var(--np-accent);
    border-width: 2px 0 0 0;
}

.np-widget.np-multi-view-schedule .fc-timegrid-now-indicator-arrow {
    border-color: var(--np-accent);
}

/* ─── Modal ─── */

.np-widget.np-multi-view-schedule .np-mv-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease;
    z-index: 100;
}

.np-widget.np-multi-view-schedule .np-mv-modal-overlay.np-mv-modal-visible {
    opacity: 1;
    visibility: visible;
}

.np-widget.np-multi-view-schedule .np-mv-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #1a1a1a;
    color: #f0f0f0;
    border: 1px solid var(--np-accent);
    padding: 18px 22px;
    width: min(420px, calc(100% - 32px));
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    z-index: 101;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease;
    /*  Kantig — explicit zero radius even though it's the
        default for div, in case some embedding page resets
        bleed in through the iframe.  */
    border-radius: 0;
}

.np-widget.np-multi-view-schedule .np-mv-modal.np-mv-modal-visible {
    opacity: 1;
    visibility: visible;
}

/* Light-theme override: when the operator picks ?theme=light
   the calendar lives on a light backdrop, so the modal should
   too — otherwise it reads as a high-contrast inversion that
   feels disconnected from the widget.  */
.np-widget.np-multi-view-schedule.np-theme-light .np-mv-modal {
    background: #f8f8f8;
    color: #1a1a1a;
}

.np-widget.np-multi-view-schedule .np-mv-modal-close {
    position: absolute;
    top: 6px;
    right: 10px;
    background: transparent;
    border: 0;
    color: inherit;
    font-size: 1.6em;
    line-height: 1;
    cursor: pointer;
    padding: 4px 8px;
}

.np-widget.np-multi-view-schedule .np-mv-modal-close:hover {
    color: var(--np-accent);
}

.np-widget.np-multi-view-schedule .np-mv-modal-title {
    margin: 0 30px 8px 0;
    font-size: 1.15em;
    font-weight: 700;
}

.np-widget.np-multi-view-schedule .np-mv-modal-time {
    margin: 0 0 12px 0;
    color: var(--np-accent);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.np-widget.np-multi-view-schedule .np-mv-modal-description {
    margin: 0 0 14px 0;
    white-space: pre-wrap;
    line-height: 1.5;
}

.np-widget.np-multi-view-schedule .np-mv-modal-link {
    display: inline-block;
    padding: 8px 14px;
    background: var(--np-accent);
    color: var(--rt-accent-fg, #fff);
    text-decoration: none;
    font-weight: 600;
}

.np-widget.np-multi-view-schedule .np-mv-modal-link:hover {
    background: var(--np-accent-hover);
    color: var(--np-fg);
}

.np-widget.np-multi-view-schedule.np-mv-no-link .np-mv-modal-link {
    display: none;
}

/* ─────────────────────────────────────────────────────────────
 *  RASTATIME 2026-05-22 — Track History widget (Wave-3).
 *
 *  Wave-1-style header (date + time picker + Now + pagesize),
 *  list of tracks (HH:MM Artist - Title), Load-More button,
 *  modal with full metadata + show context.  Kantig + accent
 *  colour conventions match the Weekly-Schedule + Multi-View
 *  widgets.
 * ───────────────────────────────────────────────────────────── */

.np-widget.np-track-history {
    /* RASTATIME 2026-09-29: the UI primary colour (Settings -> General),
       was LibreTime teal #459B8F */
    --np-accent: var(--rt-accent, #ff5d1a);
    --np-accent-hover: rgba(var(--rt-accent-rgb, 255, 93, 26), 0.4);

    max-width: 100%;
    line-height: 1.4;
    font-size: 0.92em;

    background-color: var(--np-bg-color, transparent);
    background-image: var(--np-bg-image, none);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;

    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* ─── Header band ─── */

.np-widget.np-track-history .np-th-header {
    display: flex;
    align-items: center;
    gap: 0.5em;
    padding: 6px 8px;
    border-bottom: 1px solid var(--np-fg-muted);
    color: var(--np-fg);
    flex-shrink: 0;
    font-weight: 600;
}

.np-widget.np-track-history .np-th-date,
.np-widget.np-track-history .np-th-time,
.np-widget.np-track-history .np-th-pagesize {
    background: transparent;
    border: 1px solid var(--np-fg-muted);
    color: var(--np-fg);
    padding: 3px 6px;
    font: inherit;
    /* Kantig — no border-radius */
    border-radius: 0;
}

.np-widget.np-track-history .np-th-date {
    flex: 1 1 auto;
    min-width: 0;
}

.np-widget.np-track-history .np-th-time {
    flex: 0 0 auto;
}

.np-widget.np-track-history .np-th-pagesize {
    flex: 0 0 auto;
}

.np-widget.np-track-history .np-th-now {
    background: transparent;
    border: 1px solid var(--np-fg-muted);
    color: var(--np-fg);
    padding: 3px 8px;
    font: inherit;
    cursor: pointer;
    border-radius: 0;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.85em;
    flex: 0 0 auto;
}

.np-widget.np-track-history .np-th-now:hover {
    background: var(--np-accent-hover);
}

/* ─── List ─── */

.np-widget.np-track-history .np-th-list {
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1 1 auto;
    overflow-y: auto;
    min-height: 0;
}

.np-widget.np-track-history .np-th-row {
    display: flex;
    align-items: baseline;
    gap: 0.7em;
    padding: 6px 8px;
    border-bottom: 1px dotted var(--np-fg-muted);
    color: var(--np-fg);
    cursor: pointer;
    overflow: hidden;
}

.np-widget.np-track-history .np-th-row:hover {
    background: var(--np-accent-hover);
}

.np-widget.np-track-history .np-th-row-time {
    /*  Wide enough to hold "HH:MM - HH:MM" (operator request
        2026-05-22 v14 — show both start AND end time on each
        row).  Tabular-nums keeps the column edge clean across
        rows that mix single- and double-digit hours.  */
    flex: 0 0 auto;
    min-width: 7.5em;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--np-fg-muted);
}

.np-widget.np-track-history .np-th-row-title {
    /*  Operator request 2026-05-22 v14: bold the artist - title
        so it reads as the primary content of each row (the time
        cell is the secondary identifier).  */
    flex: 1 1 auto;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 700;
}

.np-widget.np-track-history .np-th-empty {
    color: var(--np-fg-muted);
    font-style: italic;
    padding: 18px 12px;
    text-align: center;
}

/* ─── Footer (Load more) ─── */

.np-widget.np-track-history .np-th-footer {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 8px;
    border-top: 1px solid var(--np-fg-muted);
    flex-shrink: 0;
}

.np-widget.np-track-history .np-th-loadmore {
    background: transparent;
    border: 1px solid var(--np-accent);
    color: var(--np-fg);
    padding: 5px 14px;
    font: inherit;
    cursor: pointer;
    border-radius: 0;
    font-weight: 600;
}

.np-widget.np-track-history .np-th-loadmore:hover {
    background: var(--np-accent);
    color: var(--rt-accent-fg, #fff);
}

.np-widget.np-track-history .np-th-status {
    color: var(--np-fg-muted);
    font-style: italic;
    font-size: 0.9em;
}

/* ─── Modal ─── */

.np-widget.np-track-history .np-th-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease;
    z-index: 100;
}

.np-widget.np-track-history .np-th-modal-overlay.np-th-modal-visible {
    opacity: 1;
    visibility: visible;
}

.np-widget.np-track-history .np-th-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: #1a1a1a;
    color: #f0f0f0;
    border: 1px solid var(--np-accent);
    padding: 18px 22px;
    width: min(420px, calc(100% - 32px));
    max-height: calc(100vh - 32px);
    overflow-y: auto;
    z-index: 101;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.15s ease;
    border-radius: 0;
}

.np-widget.np-track-history .np-th-modal.np-th-modal-visible {
    opacity: 1;
    visibility: visible;
}

.np-widget.np-track-history.np-theme-light .np-th-modal {
    background: #f8f8f8;
    color: #1a1a1a;
}

.np-widget.np-track-history .np-th-modal-close {
    position: absolute;
    top: 6px;
    right: 10px;
    background: transparent;
    border: 0;
    color: inherit;
    font-size: 1.6em;
    line-height: 1;
    cursor: pointer;
    padding: 4px 8px;
}

.np-widget.np-track-history .np-th-modal-close:hover {
    color: var(--np-accent);
}

.np-widget.np-track-history .np-th-modal-title {
    margin: 0 30px 6px 0;
    font-size: 1.2em;
    font-weight: 700;
}

.np-widget.np-track-history .np-th-modal-artist {
    margin: 0 0 12px 0;
    font-size: 1em;
    color: var(--np-fg-muted);
}

.np-widget.np-track-history.np-theme-light .np-th-modal-artist {
    color: #555;
}

.np-widget.np-track-history .np-th-modal-time {
    margin: 0 0 8px 0;
    color: var(--np-accent);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.np-widget.np-track-history .np-th-modal-show-intro {
    /*  Caption above the show-name line ("Played in the show:")
        — muted small-caps so it reads as a label without
        competing with the show name underneath.  */
    margin: 0 0 2px 0;
    font-size: 0.85em;
    color: var(--np-fg-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.np-widget.np-track-history.np-theme-light .np-th-modal-show-intro {
    color: #555;
}

.np-widget.np-track-history .np-th-modal-show {
    margin: 0 0 14px 0;
    line-height: 1.5;
}

/*  Hide the "Played in the show:" caption when there's no show
    context (e.g. for a webstream entry that lost its instance
    binding).  The JS toggles .np-th-no-show on the body.  */
.np-widget.np-track-history.np-th-no-show .np-th-modal-show-intro,
.np-widget.np-track-history.np-th-no-show .np-th-modal-show {
    display: none;
}

.np-widget.np-track-history .np-th-modal-link {
    display: inline-block;
    padding: 8px 14px;
    background: var(--np-accent);
    color: var(--rt-accent-fg, #fff);
    text-decoration: none;
    font-weight: 600;
}

.np-widget.np-track-history .np-th-modal-link:hover {
    background: var(--np-accent-hover);
    color: var(--np-fg);
}

.np-widget.np-track-history.np-th-no-link .np-th-modal-link {
    display: none;
}
