
          html,
          body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            font-family: system-ui, -apple-system, sans-serif;
            background: transparent;
          }

          /* ---------- DARK THEME OVERRIDES ----------
             Applied when body has class "dark". Triggered either by URL param
             dark_mode=true on initial load, or at runtime via the
             "instaread:set-theme" postMessage from the parent page. */
          body.dark .player {
            background: #1a1a1a;
            box-shadow: 0 -1px 5px 0 rgba(0, 0, 0, 0.6), 0 10px 17px 0 rgba(0, 0, 0, 0.35), 0 3px 7px 0 rgba(0, 0, 0, 0.4) !important;
          }
          body.dark .meta,
          body.dark .title,
          body.dark .time {
            color: #ffffff;
          }
          body.dark .cover {
            background: #2a2a2a url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="%23bcc0c8" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>') center no-repeat !important;
            background-size: 22px 22px !important;
            background-clip: padding-box !important;
          }

          /* Keep existing wrap logic */
          /* Bar height ~84px; wrap padding drives min vertical space. Player max-width offset keeps wave width in line with the old ad layout */
          .wrap {
            width: 100%;
            min-height: 105px;
            height: auto;
            display: flex;
            align-items: flex-end;
            justify-content: var(--fp-align);
            padding: 10px 2px 10px 2px !important;
            box-sizing: border-box;
          }

          .player {
            width: 100%;
            max-width: var(--fp-max-width);
            height: 84px;
            border-radius: 20px;
            background: var(--light-bg);
            display: flex;
            align-items: center;
            padding: 0 20px;
            gap: 12px;
            box-sizing: border-box;
            box-shadow: 0 -1px 5px 0 rgba(0, 0, 0, 0.25), 0 10px 17px 0 rgba(0, 0, 0, 0.05), 0 3px 7px 0 rgba(0, 0, 0, 0.10) !important;
            container-type: inline-size;
            container-name: player;
          }

          .leftBlock {
            display: flex;
            align-items: center;
            gap: 12px;
            flex: 0 0 280px;
            min-width: 280px;
          }

          .cover {
            width: 52px !important;
            height: 52px !important;
            min-width: 52px !important;
            min-height: 52px !important;
            max-width: 52px !important;
            max-height: 52px !important;
            border-radius: 10px !important;
            background: #d1d5db url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="%239ca3af" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>') center no-repeat !important;
            background-size: 22px 22px !important;
            background-clip: padding-box !important;
            overflow: hidden !important;
            overflow-x: hidden !important;
            overflow-y: hidden !important;
            flex: 0 0 52px !important;
            flex-shrink: 0 !important;
            flex-grow: 0 !important;
            position: relative !important;
            box-sizing: border-box !important;
            padding: 0 !important;
            margin: 0 !important;
            border: none !important;
            clip-path: inset(0) !important;
          }

          .cover img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: block;
          }

          .meta {
            min-width: 0;
            color: black;
            flex: 1;
          }

          .title {
            font-size: 14px;
            font-weight: 500;
            line-height: 1.3;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
            color: black;
          }

          .controls {
            display: flex;
            align-items: center;
            gap: 10px;
            flex: 0 0 180px;
            justify-content: center;
          }

          /* Standardized button styling */
          .btn {
            width: 32px;
            height: 32px;
            border: none;
            background: transparent;
            color: var(--brand-color);
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0;
          }

          /* Dim via color/background, not opacity -- opacity on .btn would
             also fade its ::after tooltip bubble, since a pseudo-element is
             composited together with its host element as one group.
             Must mix against var(--brand-color), not currentColor: when a
             color-mix() using currentColor is assigned to the "color"
             property itself, currentColor resolves to the *inherited*
             color (no ancestor here sets one, so it falls back to default
             black) rather than this element's own brand-color -- that's
             what was turning the hover state grey instead of a lighter
             brand tint. */
          .btn:hover:not(.btnPlay) {
            color: color-mix(in srgb, var(--brand-color) 70%, transparent);
          }

          .btnPlay:hover {
            background: color-mix(in srgb, var(--brand-color) 70%, transparent);
          }

          /* Close button uses a text "✕" glyph (not an SVG), so its size depends on
             font-size. Partner page CSS resets/globals were shrinking it on some sites.
             Pin it explicitly so it stays consistent regardless of the host page. */
          #closeBtn {
            font-size: 14px !important;
            line-height: 1 !important;
            font-family: system-ui, -apple-system, sans-serif !important;
            width: 32px !important;
            height: 32px !important;
          }

          /* Mobile: let .btn rules take over — only reset font so glyph scales naturally */
          @media (max-width: 620px) {
            #closeBtn {
              width: 28px !important;
              height: 28px !important;
              font-size: 18px !important;
            }
          }

          /* Play Button specific height/width preserved */
          .btnPlay {
            background: var(--brand-color);
            color: white;
            width: 40px;
            height: 40px;
            border-radius: 50%;
            border: none;
            cursor: pointer;
          }

          .waveArea {
            flex: 1;
            display: flex;
            align-items: center;
            gap: 15px;
            min-width: 150px;
          }

          .time {
            font-size: 11px;
            color: black;
            width: 40px;
            text-align: center;
            font-variant-numeric: tabular-nums;
          }

          /* Wraps the canvas so the seek tooltip has a real anchor element.
             <canvas> doesn't reliably render ::after generated content in all
             browsers, so the tooltip is anchored on this wrapper instead --
             the wrapper now owns the flex sizing that the canvas used to have
             directly, and the canvas just fills it. */
          .seekWrap {
            position: relative;
            flex: 1 1 auto;
            min-width: 0;
            display: block;
          }

          canvas {
            width: 100%;
            height: 34px;
            cursor: pointer;
            display: block;
            /* The waveform is a drag surface, not a scroll surface. Without
               this the browser claims a touch drag as a page pan and fires
               pointercancel mid-scrub, so the drag dies as soon as it starts. */
            touch-action: none;
            /* Drag would otherwise start a text selection that follows the
               pointer across the bar. Handled here rather than with
               preventDefault() on pointerdown, which suppresses the
               compatibility mouse events that armSelfGestureUnlock()'s
               document-level click listener depends on for mobile autoplay. */
            user-select: none;
            -webkit-user-select: none;
          }

          .rightControls {
            display: flex;
            align-items: center;
            gap: 12px;
            flex: 0 0 auto;
            flex-shrink: 0;
          }

          /* Speed Button updated to look better but keeps your 60px width */
          .speedBtn {
            background-color: transparent;
            border: 1px solid var(--brand-color);
            color: var(--brand-color);
            padding: 4px 8px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 12px;
            font-weight: 600;
            width: 60px;
            text-align: center;
            flex: 0 0 60px;
          }

          /* ---------- AD COLUMN (disabled desktop + mobile): restore when re-enabling JSP ad config + HTML block ----------
          .adsBlock {
            flex: 0 0 240px;
            height: 52px;
            background: rgba(255, 255, 255, 0.05);
            border-radius: 10px;
            display: block;
            position: relative;
            padding: 1px;
            cursor: pointer;
            display: flex;
            align-items: center;
          }

          .adsBlock .widget-ad-container {
             height:35px;
                            background-image: url('./images/default_ad.png');
                            background-size: cover;
                            width: 250px;
                            background-repeat: no-repeat;
                            -webkit-background-repeat: no-repeat;
                            -moz-background-repeat: no-repeat;
                            -o-background-repeat: no-repeat;
                            background-position: contain;
                            box-shadow:none !important;
                            -webkit-background-size: contain;
                            -moz-background-size: contain;
                            -o-background-size: contain;
          }

          .adsBlock .default-instaread-ad-container {
            height: 42px;
            display: flex;
            align-items: center;
            justify-content: center;
          }

          .adsBlock .instaread-logo {
            width: 48px;
            height: auto;
          }
          ---------- */
          .adsBlock {
            display: none !important;
          }

          /* --- RESPONSIVE BREAKPOINTS (Preserved exactly) --- */
          /* @media (max-width: 1200px) {
      .adsBlock { display: none; }
    }

    @media (max-width: 850px) {
      .wrap { align-items: flex-start; padding-top: 12px; }
      .player { height: auto; flex-wrap: wrap; padding: 16px; border-radius: 20px; overflow: hidden; }
      .waveArea { order: 1; flex: 0 0 100%; margin-bottom: 4px; }
      .waveArea canvas { height: 55px; }
      .leftBlock { display: flex; order: 2; flex: 1; min-width: 0; }
      .controls { order: 3; flex: 0 0 auto; justify-content: center; }
      .rightControls { order: 4; flex: 0 0 auto; }
      #back15Btn, #fwd15Btn { display: none; }
    } */
          /* Mid-mobile: 500px → 850px
   Keep SAME 2-row layout, only improve waveform sizing */

          /* ── Container queries (fire on player's own width, not viewport) ───── */

          /* 851–895px player: tighten gaps slightly */
          @container player (min-width: 851px) and (max-width: 895px) {
            .player    { gap: 10px !important; }
            .controls  { gap: 6px !important; }
            .waveArea  { gap: 6px !important; }
            .rightControls { gap: 5px !important; }
          }

          /* 601–850px player: shrink leftBlock, reduce gaps — keep prev/next + close visible */
          @container player (min-width: 601px) and (max-width: 850px) {
            .player   { gap: 6px !important; }
            .leftBlock { flex: 0 0 150px; min-width: 0; gap: 8px; }
            .controls  { flex: 0 0 auto !important; gap: 4px !important; }
            .waveArea  { gap: 6px !important; min-width: 80px; }
            .rightControls { gap: 6px !important; flex-shrink: 0 !important; }
            .speedBtn  { width: 45px; flex: 0 0 45px; }
          }

          /* ≤600px player: compact single-row — hide prev/next, shrink cover + title */
          @container player (max-width: 600px) {
            .player       { gap: 6px !important; padding: 0 12px; }
            .leftBlock    { flex: 0 0 160px; min-width: 0; gap: 6px; }
            .cover        {
              width: 36px !important; height: 36px !important;
              flex: 0 0 36px !important; min-width: 36px !important;
              min-height: 36px !important; max-width: 36px !important;
              max-height: 36px !important;
            }
            .title        { font-size: 12px; -webkit-line-clamp: 1; }
            /* controls auto-sizes once prev/next are gone — no longer fixed 180px */
            .controls     { flex: 0 0 auto !important; gap: 4px !important; }
            .waveArea     { gap: 6px !important; min-width: 60px; }
            /* close/playlist must never be clipped */
            .rightControls { flex-shrink: 0 !important; }
            #prevBtn, #nextBtn { display: none !important; }
          }

          /* ── Viewport media queries (fire when the whole page/iframe is narrow) ── */

          /* Compact 1-row: 621–650px — all controls stay visible, elements shrink */
          @media (min-width: 621px) and (max-width: 650px) {
            .player       { padding: 0 10px; gap: 4px !important; }
            .leftBlock    { flex: 0 0 120px !important; min-width: 0 !important; gap: 6px !important; }
            .cover        {
              width: 40px !important; height: 40px !important;
              flex: 0 0 40px !important; min-width: 40px !important;
              min-height: 40px !important; max-width: 40px !important;
              max-height: 40px !important;
            }
            .title        { font-size: 11px; -webkit-line-clamp: 2; }
            .controls     { flex: 0 0 auto !important; gap: 10px !important; }
            .btn          { width: 26px; height: 26px; }
            .btnPlay      { width: 40px; height: 40px; }
            .waveArea     { min-width: 50px !important; gap: 10px !important;padding-right: 15px !important; }
            .time         { width: 32px; font-size: 10px; }
            .rightControls { gap: 10px !important; flex-shrink: 0 !important; }
            .speedBtn     { width: 40px !important; flex: 0 0 40px !important; font-size: 11px; padding: 3px 4px; }
          }

          @media (max-width: 620px) {
            .wrap {
              align-items: flex-end;
              padding: 4px;
            }

            .player {
              height: auto;
              flex-wrap: wrap;
              padding: 8px 10px;
              gap: 0px;
              justify-content: space-between;
            }

            /* Row 1: Waveform - Force full width */
            .waveArea {
              order: 1;
              flex: 0 0 100%;
              width: 100%;
              margin-bottom: 6px;
              gap: 8px;
              display: flex;
              align-items: center;
              box-sizing: border-box;
            }

            .waveArea canvas {
              flex: 1 1 auto;
              width: 100% !important;
              min-width: 0;
              height: 40px;
            }

            @media (min-width: 500px) {
              .waveArea canvas { height: 50px; }
              .speedBtn { flex: 0 0 49px !important; }
              .player   { padding: 12px 16px; }
              .title    { font-size: 14px; }
            }

            /* Row 2: Metadata */
            .leftBlock {
              order: 2;
              flex: 1;
              min-width: 0;
              display: flex;
              align-items: center;
              gap: 8px;
              margin-right: 8px;
            }

            .cover { width: 34px; height: 34px; flex: 0 0 34px; }

            .title {
              font-size: 12px;
              max-width: none;
              display: block;
              -webkit-line-clamp: unset;
              -webkit-box-orient: unset;
              white-space: nowrap;
              overflow: hidden;
              text-overflow: ellipsis;
              color: black;
            }

            /* Row 2: Controls */
            .controls {
              order: 3;
              display: flex !important;
              align-items: center;
              gap: 4px;
              flex: 0 0 auto;
            }

            #prevBtn, #nextBtn { display: none !important; }

            /* Row 2: Right Group */
            .rightControls {
              order: 4;
              display: flex !important;
              align-items: center;
              gap: 4px;
              flex: 0 0 auto;
              margin-left: 4px;
            }

            .btn    { width: 28px; height: 28px; }
            .btnPlay { width: 34px; height: 34px; }
          }

          /* Absolute minimum */
          @media (max-width: 360px) {
            .player  { padding: 6px 4px; }
            .title   { max-width: 100px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
            .speedBtn { flex: 0 0 49px !important; }
            .leftBlock { gap: 4px; }
          }

          /* Keyboard focus indicator for the seek bar. It's a <canvas>, which
             (unlike the real <button> controls elsewhere in this bar) has no
             native focus outline of its own -- needs tabindex="0" plus an
             explicit ring to be usable/visible via keyboard nav. */
          #waveCanvas:focus-visible {
            outline: 2px solid var(--brand-color);
            outline-offset: 2px;
          }

          /* Keyboard-focus tooltip, mirroring each control's data-tooltip
             attribute -- native title tooltips don't reliably show on keyboard
             focus across browsers (only reliably fires on mouse hover), so Tab
             users got no equivalent hint. Controls now carry data-tooltip
             (+ aria-label) instead of title, precisely so the native OS
             tooltip never fires alongside this one -- with title still present
             the browser's own light-grey tooltip would kick in after ~1s of
             hover and visually clash/compete with this bubble; dropping title
             makes this the only tooltip, on both hover and focus.
             position:relative is unconditional (not just on :focus-visible)
             since none of these controls had an explicit position before --
             it's a layout no-op without added top/left/etc, and is required
             so the ::after bubble anchors correctly. The seek bar's tooltip is
             anchored on .seekWrap rather than #waveCanvas -- <canvas> doesn't
             reliably paint ::after generated content, so the wrapper div is
             the actual anchor; :has(#waveCanvas:focus-visible) mirrors the
             canvas's own keyboard-focus state onto it since :focus-visible
             itself doesn't bubble to ancestors the way :hover does. */
          .btn,
          .speedBtn,
          .seekWrap {
            position: relative;
          }

          .btn::after,
          .speedBtn::after,
          .seekWrap::after {
            content: attr(data-tooltip);
            position: absolute;
            bottom: calc(100% + 8px);
            left: 50%;
            transform: translateX(-50%);
            background: #5f6368;
            color: #ffffff;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            font-weight: 400;
            border: none;
            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
            font-size: 11px;
            line-height: 1.4;
            padding: 3px 7px;
            border-radius: 4px;
            white-space: nowrap;
            pointer-events: none;
            z-index: 10000;
            /* Resting state. Both the hover and focus rules below drive the
               bubble entirely through fpTooltipAutoHide, so nothing ever
               transitions between states here -- no `transition` declaration,
               because with no rule setting opacity:1 it could never fire. */
            opacity: 0;
            visibility: hidden;
          }

          /* Hover: wait ~1s before showing, then dismiss itself after ~2s.
             This has to be a keyframe animation rather than a transition --
             a transition only animates a state *change*, so while the pointer
             stays on the control :hover remains true and opacity:1 held
             forever, which is exactly the bug this replaces. An animation owns
             the whole timeline instead. Over the 2.3s run: 0-4% fades in,
             4-91% holds (~2s visible), 91-100% fades out. `forwards` keeps the
             final hidden frame instead of snapping back to the 0% frame, and
             the 1s delay uses the element's own (hidden) base style since the
             fill is not `backwards`. */
          @keyframes fpTooltipAutoHide {
            0%   { opacity: 0; visibility: visible; }
            4%   { opacity: 1; visibility: visible; }
            91%  { opacity: 1; visibility: visible; }
            100% { opacity: 0; visibility: hidden; }
          }

          /* Deliberately NOT gated behind (hover: hover) and (pointer: fine).
             Touch has no true hover -- :hover latches on tap and would normally
             leave the bubble stuck until you tap elsewhere -- but the auto-hide
             animation already retires it after ~2s, so the latch resolves
             itself and the tooltip stays available on mobile. That gate also
             evaluates false in Chrome DevTools device emulation, which hid the
             tooltip from mouse hover while testing a mobile viewport.
             The <=850px rule further down handles the real mobile problem, the
             bubble being clipped for want of headroom.
             :not(:focus-visible) hands keyboard focus to the rule below. Both
             rules animate, so without it a control that is focused *and*
             hovered would match both; they carry different delays, so which one
             won would decide whether the bubble appears at once or after 1s. */
          .btn:hover:not(:focus-visible)::after,
          .speedBtn:hover:not(:focus-visible)::after {
            animation: fpTooltipAutoHide 2.3s ease 1s forwards;
          }

          /* Split out rather than joined to the list above: this selector
             needs :has(), and an unsupported selector invalidates the entire
             rule it sits in, which would take the button tooltips down with
             it on any browser that lacks support. */
          .seekWrap:hover:not(:has(#waveCanvas:focus-visible))::after {
            animation: fpTooltipAutoHide 2.3s ease 1s forwards;
          }

          /* Keyboard focus: show immediately -- no reason to make Tab users
             wait -- then dismiss after the same ~2s as hover. Same keyframes
             with no delay, so the only difference from hover is when the
             timeline starts; the ~90ms fade-in reads as instant. Tabbing away
             and back restarts the animation, since the element stops and
             re-matches the selector. */
          .btn:focus-visible::after,
          .speedBtn:focus-visible::after {
            animation: fpTooltipAutoHide 2.2s ease 0s forwards;
          }

          /* Split out for the same :has() reason as the hover rule above. */
          .seekWrap:has(#waveCanvas:focus-visible)::after {
            animation: fpTooltipAutoHide 2.2s ease 0s forwards;
          }

          /* THE mobile fix. Narrow layouts stack waveArea as the top row, so
             the seek bar ends up near the very top of the iframe. Measured at
             390x175 (the height updateIframeHeight() requests below 660px):
             .wrap starts at y=0, .player at y=10, .seekWrap at y=18, and the
             bubble is 21.4px tall sitting 8px clear of its anchor -- so
             unflipped its top lands at 18-8-21.4 = -11.4px, outside
             body{overflow:hidden}, and it got clipped back into the bar.
             The slack is all *below*: the wrap is 118px inside a 175px iframe.
             So flip the bubble under the anchor, where it draws over the
             controls row and stays inside the iframe box.
             Below rather than centred over the canvas on purpose: the waveform
             is the thing being scrubbed, so it must stay visible during the
             drag; the controls row can afford to be covered for ~2s. The
             bubble is pointer-events:none, so the play button underneath stays
             tappable the whole time. Applies to hover and keyboard focus
             alike. */
          @media (max-width: 850px) {
            .seekWrap::after {
              bottom: auto;
              top: calc(100% + 8px);
            }
          }
