/* The audio player of a note (lib/audio-player.php, js/audio-player.js).
   Loaded by every page that can draw a note's audio: the player frame of the
   editor, a public share, the journal.

   The element is its own container: the same markup is drawn in a 150px
   attachment tile and in a 420px note, so what it shows at a given width is
   decided by @container rules and never by the page around it. */

.pz-audio {
    position: relative;
    container-type: inline-size;
    container-name: pz-audio;
    --pz-audio-progress: 0%;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    /* The same box wherever a recording is read. On a page that renders the
       player inline (a public share, the journal) this is what keeps it from
       stretching across the column; in the editor's frame the frame is
       already this wide. */
    max-width: var(--pz-audio-player-width);
    height: var(--pz-audio-player-height);
    margin: 8px 0;
    padding: 0 10px;
    border: 1px solid var(--pz-border-light);
    border-radius: var(--pz-radius-lg);
    background: var(--pz-surface);
    color: var(--pz-text);
    font-family: inherit;
    overflow: hidden;
}

.pz-audio * {
    box-sizing: border-box;
}

/* The element that actually plays. It is never the one on screen: a browser
   draws its own controls for it, and inside a contenteditable Chrome draws
   none at all, which is the whole reason the editor frames this page. */
.pz-audio .pz-audio-el {
    display: none;
}

/* --- Play / pause --------------------------------------------------------- */

/* Square, not round: a round button is Telegram's, and everything else in
   Poznote is a rounded square. It is also what lets the cover art stand in
   for it, the two being the same box. */
.pz-audio .pz-audio-play {
    position: relative;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin: 0;
    padding: 0;
    overflow: hidden;
    border: 0;
    border-radius: var(--pz-radius-sm);
    background: var(--pz-accent);
    color: var(--pz-text-inverse);
    cursor: pointer;
    transition: background-color 0.12s ease;
}

.pz-audio .pz-audio-play:hover,
.pz-audio .pz-audio-play:focus-visible {
    background: var(--pz-accent-hover);
}

.pz-audio.is-error .pz-audio-play,
.pz-audio .pz-audio-play[disabled] {
    background: var(--pz-disabled);
    cursor: default;
}

/* A triangle and a pair of bars, drawn rather than loaded: css/lucide.css is
   870 KB of icon data and this player is framed once per recording. */
.pz-audio .pz-audio-play-icon {
    display: block;
    width: 0;
    height: 0;
    margin-left: 3px;
    border-top: 7px solid transparent;
    border-bottom: 7px solid transparent;
    border-left: 12px solid currentColor;
}

.pz-audio.is-playing .pz-audio-play-icon {
    width: 12px;
    height: 14px;
    margin-left: 0;
    border: 0;
    background:
        linear-gradient(currentColor, currentColor) left / 4px 100% no-repeat,
        linear-gradient(currentColor, currentColor) right / 4px 100% no-repeat;
}

/* --- Cover art ------------------------------------------------------------ */

/* The cover fills the button. At rest that is all there is to see; the glyph
   and its scrim come up under the pointer, and stay up while the recording
   plays so pause is never hidden. A screen without hover shows them always:
   there is no second place to tap. */
.pz-audio .pz-audio-cover {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: var(--pz-surface-hover);
}

.pz-audio-has-cover .pz-audio-play {
    background: var(--pz-surface-hover);
}

.pz-audio-has-cover .pz-audio-play:hover,
.pz-audio-has-cover .pz-audio-play:focus-visible {
    background: var(--pz-surface-hover);
}

/* Under the glyph, never over it: ::before paints before the button's own
   children, ::after would paint on top of them. */
.pz-audio-has-cover .pz-audio-play::before {
    content: "";
    position: absolute;
    z-index: 1;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: rgba(var(--pz-shadow-rgb), 0.5);
    opacity: 0;
    transition: opacity 0.12s ease;
}

/* The cover is positioned too and comes later in the button, so without a
   z-index it paints over the scrim and the cover never darkens. The glyph
   sits above both, and takes the highlight channel rather than the accent's
   label colour: it is drawn on a darkened photo, whatever the theme. */
.pz-audio-has-cover .pz-audio-play-icon {
    position: relative;
    z-index: 2;
    color: rgb(var(--pz-highlight-rgb));
    opacity: 0;
    transition: opacity 0.12s ease;
}

.pz-audio-has-cover .pz-audio-play:hover::before,
.pz-audio-has-cover .pz-audio-play:focus-visible::before,
.pz-audio-has-cover.is-playing .pz-audio-play::before,
.pz-audio-has-cover .pz-audio-play:hover .pz-audio-play-icon,
.pz-audio-has-cover .pz-audio-play:focus-visible .pz-audio-play-icon,
.pz-audio-has-cover.is-playing .pz-audio-play-icon {
    opacity: 1;
}

@media (hover: none) {
    .pz-audio-has-cover .pz-audio-play::before,
    .pz-audio-has-cover .pz-audio-play-icon {
        opacity: 1;
    }
}

/* A file the browser refused keeps its cover, dimmed: the square is no longer
   a button, and it should not look like one. */
.pz-audio-has-cover.is-error .pz-audio-cover {
    opacity: 0.45;
}

.pz-audio-has-cover.is-error .pz-audio-play::before,
.pz-audio-has-cover.is-error .pz-audio-play-icon {
    opacity: 0;
}

/* --- Title, artist, waveform ---------------------------------------------- */

.pz-audio .pz-audio-body {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 3px;
    min-width: 0;
}

.pz-audio .pz-audio-tags {
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.25;
}

.pz-audio .pz-audio-title {
    overflow: hidden;
    color: var(--pz-text);
    font-size: 13px;
    font-weight: var(--pz-weight-medium, 500);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pz-audio .pz-audio-artist {
    overflow: hidden;
    color: var(--pz-text-muted);
    font-size: 11.5px;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The file the browser has refused: the track has no samples to show and the
   clock no time to count, so the reason takes their place inside the same box.
   Two lines at most, since the box keeps its height. */
.pz-audio .pz-audio-error {
    display: none;
    margin: 0;
    overflow: hidden;
    color: var(--pz-text-muted);
    font-size: 11.5px;
    line-height: 1.3;
}

/* Clamped rather than cut: in a narrow tile the sentence needs more lines than
   the box has, and an ellipsis says so where a line sliced in half does not.
   The whole sentence stays in the title attribute (js/audio-player.js). */
.pz-audio.is-error .pz-audio-error {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
}

.pz-audio.is-error .pz-audio-track,
.pz-audio.is-error .pz-audio-side {
    display: none;
}

.pz-audio .pz-audio-track {
    position: relative;
    height: 26px;
    cursor: pointer;
    touch-action: none;
}

/* The line of a tagged player: a little taller than it looks, so the click
   that seeks does not ask for the 4px of the bar itself. */
.pz-audio-tagged .pz-audio-track {
    height: 18px;
    margin-top: -1px;
}

/* The words sit low, next to the line they describe, rather than up against
   the top of the box with the hit area of the line opening a gap under them.
   The 4px they move down are taken from the gap, so the body keeps its height
   and the line stays where it was. */
.pz-audio-tagged .pz-audio-body {
    gap: 0;
}

.pz-audio-tagged .pz-audio-tags {
    margin-top: 4px;
}

.pz-audio .pz-audio-track:focus-visible {
    border-radius: var(--pz-radius-sm);
    outline: 2px solid var(--pz-accent);
    outline-offset: 2px;
}

/* Until the samples have been read, and for a file too large to read, the
   track is the plain bar a player has always had. */
.pz-audio .pz-audio-track.is-plain .pz-audio-wave {
    top: 50%;
    height: 4px;
    margin-top: -2px;
    border-radius: var(--pz-radius-sm);
    background: var(--pz-border);
}

.pz-audio .pz-audio-track.is-plain .pz-audio-wave-played {
    background: var(--pz-accent);
}

.pz-audio .pz-audio-wave {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: flex;
    align-items: center;
    gap: 2px;
}

/* The played half is the same bars in the accent colour, clipped to where the
   playhead is: one set of bars cannot be two colours at an arbitrary point. */
.pz-audio .pz-audio-wave-played {
    clip-path: inset(0 calc(100% - var(--pz-audio-progress)) 0 0);
}

.pz-audio .pz-audio-bar {
    flex: 1 1 0;
    min-width: 2px;
    max-width: 3px;
    border-radius: var(--pz-radius-sm);
    background: var(--pz-border-strong);
    opacity: 0.55;
}

.pz-audio .pz-audio-wave-played .pz-audio-bar {
    background: var(--pz-accent);
    opacity: 1;
}

/* --- Time and speed ------------------------------------------------------- */

.pz-audio .pz-audio-side {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
}

.pz-audio .pz-audio-time {
    display: flex;
    gap: 3px;
    color: var(--pz-text-muted);
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.pz-audio .pz-audio-total:not(:empty)::before {
    content: "/";
    margin-right: 3px;
    opacity: 0.6;
}

/* --- Volume ---------------------------------------------------------------

   A desktop control only. A phone has hardware keys for this, and iOS ignores
   a volume set from script outright, so the speaker would be a button that
   does nothing there. The slider slides out over the waveform instead of
   taking a place of its own on the row: the player is framed in an iframe of
   its own size in the editor, so nothing may be drawn outside its box. */
.pz-audio .pz-audio-volume {
    position: relative;
    display: none;
    align-items: center;
}

@media (hover: hover) and (pointer: fine) {
    .pz-audio .pz-audio-volume {
        display: flex;
    }
}

.pz-audio .pz-audio-volume-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: var(--pz-radius-sm);
    background: transparent;
    color: var(--pz-text-muted);
    cursor: pointer;
}

.pz-audio .pz-audio-volume-btn:hover,
.pz-audio .pz-audio-volume-btn:focus-visible {
    color: var(--pz-accent-text, var(--pz-accent));
}

/* Drawn as a mask so it takes the colour of the row, like the icons elsewhere
   in the app; css/lucide.css is 870 KB and this frame loads once per file. */
.pz-audio .pz-audio-volume-icon {
    width: 16px;
    height: 16px;
    background: currentColor;
    -webkit-mask: var(--pz-icon-volume) center / 16px 16px no-repeat;
    mask: var(--pz-icon-volume) center / 16px 16px no-repeat;
}

.pz-audio .pz-audio-volume.is-muted .pz-audio-volume-icon {
    -webkit-mask-image: var(--pz-icon-volume-off);
    mask-image: var(--pz-icon-volume-off);
}

.pz-audio .pz-audio-volume-pop {
    position: absolute;
    top: 50%;
    right: 100%;
    display: flex;
    align-items: center;
    width: 76px;
    height: 24px;
    margin-top: -12px;
    /* The air between the slider and the speaker is padding of the pop, not a
       margin: the pointer crossing it is still on the volume box, so the
       slider does not start to fade on the way from one to the other. */
    padding: 0 6px 0 4px;
    /* The player's own ground, so the slider covers the clock under it
       rather than being drawn across the digits. */
    background: var(--pz-surface);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.12s ease, visibility 0.12s ease;
}

/* Hover, a drag in progress or keyboard focus (.is-open, js/audio-player.js).
   Not :focus-within: a click leaves the slider focused, and the pop stayed
   up until something else was clicked. */
.pz-audio .pz-audio-volume:hover .pz-audio-volume-pop,
.pz-audio .pz-audio-volume.is-open .pz-audio-volume-pop {
    opacity: 1;
    visibility: visible;
}

/* The same line as the seek bar of a tagged player: a 4px track in the border
   colour, filled with the accent up to the level, no knob. The input is
   taller than the line so a click does not have to land on the 4px. */
.pz-audio .pz-audio-volume-slider {
    --pz-audio-volume-fill: 100%;
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    height: 18px;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    appearance: none;
    cursor: pointer;
}

.pz-audio .pz-audio-volume-slider:focus {
    outline: none;
}

.pz-audio .pz-audio-volume-slider:focus-visible {
    border-radius: var(--pz-radius-sm);
    outline: 2px solid var(--pz-accent);
    outline-offset: 1px;
}

.pz-audio .pz-audio-volume-slider::-webkit-slider-runnable-track {
    height: 4px;
    border-radius: var(--pz-radius-sm);
    background: linear-gradient(to right,
        var(--pz-accent) 0 var(--pz-audio-volume-fill),
        var(--pz-border) var(--pz-audio-volume-fill) 100%);
}

.pz-audio .pz-audio-volume-slider::-moz-range-track {
    height: 4px;
    border-radius: var(--pz-radius-sm);
    background: var(--pz-border);
}

.pz-audio .pz-audio-volume-slider::-moz-range-progress {
    height: 4px;
    border-radius: var(--pz-radius-sm);
    background: var(--pz-accent);
}

/* A knob of no width: the browser still needs one to map the pointer to a
   value, the reader only needs the line. */
.pz-audio .pz-audio-volume-slider::-webkit-slider-thumb {
    width: 1px;
    height: 4px;
    border: 0;
    background: transparent;
    appearance: none;
}

.pz-audio .pz-audio-volume-slider::-moz-range-thumb {
    width: 1px;
    height: 4px;
    border: 0;
    border-radius: 0;
    background: transparent;
}

/* Rounded square like the play button, and one width for every speed: sized
   on its own label, "1.25×" was wider than "1×" and pushed the clock and the
   speaker sideways at each click. */
.pz-audio .pz-audio-speed {
    flex: 0 0 auto;
    width: 38px;
    height: 22px;
    padding: 0;
    border: 1px solid var(--pz-border);
    border-radius: var(--pz-radius-sm);
    text-align: center;
    white-space: nowrap;
    background: transparent;
    color: var(--pz-text-muted);
    font-family: inherit;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
}

.pz-audio .pz-audio-speed:hover,
.pz-audio .pz-audio-speed:focus-visible {
    border-color: var(--pz-accent);
    color: var(--pz-accent-text, var(--pz-accent));
}

.pz-audio .pz-audio-speed.is-changed {
    border-color: var(--pz-accent);
    color: var(--pz-accent-text, var(--pz-accent));
}

/* --- Hover readout --------------------------------------------------------

   The time under the pointer on the seek bar, the level under it on the
   volume slider (js/audio-player.js places it). It lives inside the player's
   box, above the control it reads: in the editor the player is an iframe of
   its own size, and nothing can be drawn past its edges. */
.pz-audio .pz-audio-tip {
    position: absolute;
    z-index: 3;
    top: 0;
    left: 0;
    padding: 1px 5px;
    border: 1px solid var(--pz-border);
    border-radius: var(--pz-radius-sm);
    background: var(--pz-bg);
    color: var(--pz-text);
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    line-height: 15px;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
}

.pz-audio .pz-audio-tip.is-visible {
    opacity: 1;
    visibility: visible;
}

/* --- Narrow players ------------------------------------------------------- */

/* An attachment tile is 150px wide and a note can be half that on a phone:
   each piece leaves in the order it can be done without, the waveform last. */
@container pz-audio (max-width: 340px) {
    .pz-audio .pz-audio-volume {
        display: none;
    }
}

@container pz-audio (max-width: 300px) {
    .pz-audio .pz-audio-speed {
        display: none;
    }
}

/* The cover stays: it is the play button, and a square that was going to be
   drawn anyway costs nothing. Only the words go. */
@container pz-audio (max-width: 250px) {
    .pz-audio .pz-audio-tags {
        display: none;
    }

    .pz-audio .pz-audio-track {
        height: 26px;
    }
}

@container pz-audio (max-width: 190px) {
    .pz-audio {
        gap: 8px;
        padding: 0 8px;
    }

    /* Not enough room for "0:07 / 3:48": the length stays, the position goes.
       The bar already shows where the playhead is, and the length is what a
       reader wants from a tile before pressing anything. */
    .pz-audio .pz-audio-elapsed {
        display: none;
    }

    /* Same :not(:empty) as the rule it undoes, or that one outweighs this
       one and the player reads "/ 0:24". */
    .pz-audio .pz-audio-total:not(:empty)::before {
        content: none;
        margin-right: 0;
    }

    .pz-audio .pz-audio-play {
        width: 32px;
        height: 32px;
    }

    .pz-audio .pz-audio-play-icon {
        margin-left: 2px;
        border-top-width: 6px;
        border-bottom-width: 6px;
        border-left-width: 10px;
    }

    .pz-audio.is-playing .pz-audio-play-icon {
        width: 10px;
        height: 12px;
        margin-left: 0;
    }
}

@container pz-audio (max-width: 140px) {
    .pz-audio .pz-audio-time {
        display: none;
    }
}
