/* ==========================================================================
   Raconteur

   Three regions: a slim header, a library rail, and the book being listened to.
   The transport is the bottom row of the same grid, so it is always on screen
   without being positioned over the content.

   The stylesheet carries two things: this file's own classes, and the handful of
   Bulma class names that script.js emits when it builds cards, chapters and
   notifications. Bulma itself is no longer loaded -- it came from a CDN, which
   put a third party in the path of a page served from the house.
   ========================================================================== */

:root {
    --bg: #0d0e13;
    --panel: #15171f;
    --panel-2: #1c1f2a;
    --raised: #232735;
    --line: #2a2e3d;
    --text: #e9eaef;
    --muted: #9aa1b5;
    --faint: #6d7386;

    --accent: #e0a458;
    --accent-ink: #1a1206;
    --accent-soft: rgba(224, 164, 88, 0.14);

    --ok: #4caf7d;
    --info: #6c8ee0;
    --warn: #e0a458;
    --danger: #e0685c;

    --radius: 12px;
    --radius-sm: 8px;
    --rail: 340px;
    --tap: 44px;

    --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    color-scheme: dark;
}

html[data-theme="light"] {
    --bg: #f4f1ea;
    --panel: #fffdf9;
    --panel-2: #efeae0;
    --raised: #e7e1d5;
    --line: #ddd5c7;
    --text: #23252c;
    --muted: #616777;
    --faint: #8a8f9e;

    --accent: #b0751f;
    --accent-ink: #fff8ec;
    --accent-soft: rgba(176, 117, 31, 0.12);

    --ok: #2f7d55;
    --info: #3f61b5;
    --warn: #b0751f;
    --danger: #b34a3f;

    color-scheme: light;
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

html,
body {
    height: 100%;
    margin: 0;
}

body {
    background: var(--bg);
    color: var(--text);
    font-family: var(--sans);
    font-size: 15px;
    line-height: 1.45;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
}

/* script.js drives visibility by toggling this class, so it has to win. */
.is-hidden {
    display: none !important;
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.9;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Play and pause are solid shapes, not strokes. */
.btn-play svg,
.panel-block.is-active .panel-icon {
    fill: currentColor;
    stroke: none;
}

/* ==========================================================================
   Shell
   ========================================================================== */

.app {
    display: grid;
    grid-template-columns: var(--rail) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
        "top top"
        "rail stage"
        "transport transport";
    height: 100dvh;
}

.topbar {
    grid-area: top;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 14px;
    height: 56px;
    background: var(--panel);
    border-bottom: 1px solid var(--line);
}

.topbar__spacer {
    flex: 1;
}

.topbar__nav {
    display: none;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    font-family: var(--serif);
    font-size: 20px;
    letter-spacing: 0.01em;
    color: var(--text);
}

.brand__mark {
    width: 22px;
    height: 22px;
    border-radius: 5px;
}

/* ==========================================================================
   Rail
   ========================================================================== */

.rail {
    grid-area: rail;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--panel);
    border-right: 1px solid var(--line);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.rail__search {
    position: sticky;
    top: 0;
    z-index: 2;
    padding: 14px 14px 10px;
    background: linear-gradient(var(--panel) 78%, transparent);
}

.search {
    position: relative;
    display: flex;
    align-items: center;
}

.search__icon {
    position: absolute;
    left: 11px;
    color: var(--faint);
    pointer-events: none;
}

.search__input {
    width: 100%;
    height: 40px;
    padding: 0 38px 0 37px;
    color: var(--text);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 10px;
    font: inherit;
    font-size: 14px;
    appearance: none;
}

.search__input::placeholder {
    color: var(--faint);
}

.search__input:focus {
    border-color: var(--accent);
    background: var(--bg);
    outline: none;
}

.search__input::-webkit-search-cancel-button {
    display: none;
}

.search__clear {
    position: absolute;
    right: 6px;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    color: var(--muted);
    background: none;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}

.search__clear:hover {
    color: var(--text);
    background: var(--raised);
}

.search__clear svg {
    width: 15px;
    height: 15px;
}

.rail__search-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 9px;
    min-height: 26px;
}

.seg {
    display: inline-flex;
    margin-left: auto;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 8px;
    overflow: hidden;
}

.seg__btn {
    padding: 4px 10px;
    color: var(--muted);
    background: none;
    border: 0;
    font: inherit;
    font-size: 12px;
    cursor: pointer;
}

.seg__btn + .seg__btn {
    border-left: 1px solid var(--line);
}

.seg__btn:hover {
    color: var(--text);
    background: var(--raised);
}

.rail__library {
    padding: 4px 10px 22px;
}

.rail__library-head,
.chapters__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding: 6px 4px 8px;
}

.eyebrow {
    margin: 0;
    color: var(--faint);
    font-family: var(--sans);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
}

.legend {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    margin: 0 0 12px;
    padding: 0 4px;
    list-style: none;
    color: var(--faint);
    font-size: 11px;
}

.legend li {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--faint);
}

.dot--unread { background: var(--faint); }
.dot--progress { background: var(--info); }
.dot--done { background: var(--ok); }
.dot--playing { background: var(--accent); }

/* ==========================================================================
   Library — author groups and book cards
   ========================================================================== */

.author-group {
    margin-bottom: 6px;
}

.author-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 9px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    user-select: none;
}

.author-header:hover {
    background: var(--panel-2);
}

.author-header strong {
    color: var(--text);
    font-size: 13px;
    font-weight: 650;
    letter-spacing: 0.01em;
}

.author-header .icon {
    display: grid;
    place-items: center;
    width: 18px;
    color: var(--faint);
    font-size: 10px;
    transition: transform 0.15s ease;
}

.author-books {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-top: 2px;
    padding-left: 4px;
}

/* A book card. script.js adds has-background-{light,info,success,primary} for
   status, which becomes an accent bar plus a dot rather than a wall of pastel --
   colour alone was the only status signal before, with a legend needed to
   explain it. */
.box {
    position: relative;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}

.audiobook-card {
    padding: 9px 10px;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}

.audiobook-card::before {
    content: "";
    position: absolute;
    left: 0;
    top: 10px;
    bottom: 10px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--faint);
}

.audiobook-card.has-background-info::before { background: var(--info); }
.audiobook-card.has-background-success::before { background: var(--ok); }
.audiobook-card.has-background-primary::before { background: var(--accent); }

.audiobook-card:hover {
    background: var(--raised);
    border-color: var(--line);
}

.audiobook-card.is-current {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.media {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

.media-left,
.media-right {
    flex: 0 0 auto;
}

.media-content {
    flex: 1 1 auto;
    min-width: 0;
}

.image {
    display: block;
    overflow: hidden;
    background: var(--raised);
    border-radius: 6px;
}

.image.is-64x64 {
    width: 46px;
    height: 46px;
}

.image.is-128x128 {
    width: 112px;
    height: 112px;
}

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

.audiobook-card strong {
    display: block;
    color: var(--text);
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.3;
    /* Two lines, then ellipsis: a long title must not push the button row. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ==========================================================================
   Stage
   ========================================================================== */

.stage {
    grid-area: stage;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 22px 26px 26px;
}

.empty {
    display: grid;
    place-items: center;
    gap: 6px;
    height: 100%;
    color: var(--faint);
    text-align: center;
}

.empty__mark {
    width: 54px;
    height: 54px;
    opacity: 0.35;
    filter: grayscale(1);
}

.empty h1 {
    margin: 4px 0 0;
    color: var(--muted);
    font-family: var(--serif);
    font-size: 22px;
    font-weight: 400;
}

.empty p {
    margin: 0;
    font-size: 13px;
}

/* script.js reveals #current-audiobook when a book is chosen; the placeholder
   goes away with it, which is done here rather than by editing the script. */
.stage:has(#current-audiobook:not(.is-hidden)) #stage-empty {
    display: none;
}

/* --- The chosen book ------------------------------------------------------ */

.hero {
    display: flex;
    align-items: flex-end;
    gap: 20px;
    margin-bottom: 26px;
}

.hero__cover {
    flex: 0 0 auto;
    width: 132px;
    height: 132px;
    object-fit: cover;
    background: var(--raised);
    border-radius: 10px;
    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.35);
}

.hero__text {
    min-width: 0;
    padding-bottom: 4px;
}

.hero__title {
    margin: 0;
    color: var(--text);
    font-family: var(--serif);
    font-size: clamp(24px, 3vw, 34px);
    font-weight: 500;
    line-height: 1.15;
    letter-spacing: -0.01em;
}

.hero__author {
    margin: 7px 0 0;
    color: var(--muted);
    font-size: 14px;
}

.hero__actions {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 14px;
}

.hero__status {
    color: var(--muted);
    font-size: 12px;
}

/* --- Continue listening --------------------------------------------------- */

#continue-listening {
    margin-bottom: 22px;
}

#continue-listening-card .box {
    background: var(--accent-soft);
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}

#continue-listening-card .has-text-grey-dark {
    color: var(--muted);
}

/* --- Chapters ------------------------------------------------------------- */

.chapters {
    margin-bottom: 8px;
}

.chapters__body {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--panel);
}

.tracks {
    display: flex;
    flex-direction: column;
}

/* One chapter. script.js builds these as panel-block anchors with panel-icon,
   the title, an optional damaged tag and the duration pushed right by ml-auto. */
.panel-block {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 14px;
    min-height: 46px;
    color: var(--text);
    text-decoration: none;
    border-bottom: 1px solid var(--line);
    cursor: pointer;
}

.panel-block:last-child {
    border-bottom: 0;
}

.panel-block:hover {
    background: var(--panel-2);
}

.panel-block.is-active {
    background: var(--accent-soft);
    box-shadow: inset 3px 0 0 var(--accent);
}

.panel-icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 24px;
    color: var(--faint);
    font-family: var(--mono);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

.panel-block.is-active .panel-icon {
    color: var(--accent);
}

.panel-block > span:nth-of-type(2) {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13.5px;
    line-height: 1.35;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.panel-block .tag.is-light {
    flex: 0 0 auto;
    color: var(--muted);
    background: none;
    padding: 0;
    font-family: var(--mono);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Transport
   ========================================================================== */

.transport {
    grid-area: transport;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 8px 18px 12px;
    background: var(--panel);
    border-top: 1px solid var(--line);
    box-shadow: 0 -12px 30px rgba(0, 0, 0, 0.18);
}

.transport__scrub {
    display: flex;
    align-items: center;
    gap: 14px;
}

.time {
    flex: 0 0 auto;
    min-width: 108px;
    color: var(--muted);
    font-family: var(--mono);
    font-size: 11.5px;
    font-variant-numeric: tabular-nums;
}

.scrub {
    flex: 1 1 auto;
    height: 20px;
    background: none;
    appearance: none;
    cursor: pointer;
}

/* The filled part of the track is drawn with a gradient stop driven by --pct,
   which ui.js keeps in step with the audio element. */
.scrub::-webkit-slider-runnable-track {
    height: 4px;
    border-radius: 2px;
    background: linear-gradient(to right, var(--accent) var(--pct, 0%), var(--raised) var(--pct, 0%));
}

.scrub::-moz-range-track {
    height: 4px;
    border-radius: 2px;
    background: var(--raised);
}

.scrub::-moz-range-progress {
    height: 4px;
    border-radius: 2px;
    background: var(--accent);
}

.scrub::-webkit-slider-thumb {
    appearance: none;
    width: 13px;
    height: 13px;
    margin-top: -4.5px;
    border: 0;
    border-radius: 50%;
    background: var(--text);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

.scrub::-moz-range-thumb {
    width: 13px;
    height: 13px;
    border: 0;
    border-radius: 50%;
    background: var(--text);
}

.transport__row {
    display: flex;
    align-items: center;
    gap: 16px;
}

.transport__now {
    flex: 1 1 auto;
    min-width: 0;
}

.transport__title {
    overflow: hidden;
    color: var(--text);
    font-size: 13.5px;
    font-weight: 600;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.transport__book {
    overflow: hidden;
    color: var(--faint);
    font-size: 12px;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.transport__actions {
    display: flex;
    align-items: center;
    gap: 4px;
}

.icon-btn,
.btn-icon,
.btn-play,
.btn-rate {
    display: grid;
    place-items: center;
    color: var(--muted);
    background: none;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.icon-btn {
    width: 38px;
    height: 38px;
}

.btn-icon {
    width: var(--tap);
    height: var(--tap);
}

.icon-btn:hover,
.btn-icon:hover {
    color: var(--text);
    background: var(--panel-2);
}

.btn-play {
    width: 54px;
    height: 54px;
    margin: 0 6px;
    color: var(--accent-ink);
    background: var(--accent);
}

.btn-play:hover {
    filter: brightness(1.08);
}

.btn-play svg {
    width: 24px;
    height: 24px;
}

.btn-rate {
    width: var(--tap);
    height: 34px;
    margin-left: 6px;
    border-radius: 8px;
    color: var(--muted);
    font-family: var(--mono);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

.btn-rate:hover {
    color: var(--text);
    background: var(--panel-2);
}

.btn-rate.is-fast {
    color: var(--accent);
}

#player,
#progress-bar {
    display: none;
}

/* ==========================================================================
   Buttons, tags and other pieces script.js emits
   ========================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    min-height: 36px;
    color: var(--text);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-radius: 9px;
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}

.btn:hover {
    background: var(--raised);
}

.btn svg {
    width: 15px;
    height: 15px;
}

.btn--ghost {
    background: transparent;
}

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 30px;
    min-height: 30px;
    padding: 0 6px;
    color: var(--muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 7px;
    font: inherit;
    font-size: 12px;
    cursor: pointer;
}

.button:hover {
    color: var(--text);
    background: var(--raised);
}

.button.is-danger:hover {
    color: var(--danger);
}

.button.is-success:hover {
    color: var(--ok);
}

.button.is-warning:hover {
    color: var(--warn);
}

.button .icon {
    display: grid;
    place-items: center;
    font-size: 14px;
    line-height: 1;
}

.button.is-loading {
    color: var(--muted);
    padding: 0 14px;
}

.button.is-loading::after {
    content: "";
    width: 12px;
    height: 12px;
    margin-left: 8px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

.buttons {
    display: flex;
    gap: 2px;
}

.buttons.has-addons .button + .button {
    border-left: 1px solid var(--line);
    border-radius: 0;
}

.buttons.has-addons .button:first-child {
    border-radius: 7px 0 0 7px;
}

.buttons.has-addons .button:last-child {
    border-radius: 0 7px 7px 0;
}

.tag {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    color: var(--muted);
    background: var(--raised);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
}

.tag.is-light {
    padding: 1px 6px;
    color: var(--faint);
    background: var(--panel-2);
    font-weight: 500;
}

.tag.is-success {
    color: var(--ok);
    background: color-mix(in srgb, var(--ok) 15%, transparent);
}

.tag.is-warning {
    color: var(--warn);
    background: color-mix(in srgb, var(--warn) 15%, transparent);
}

.tag.is-info {
    color: var(--info);
    background: color-mix(in srgb, var(--info) 15%, transparent);
}

.tag.is-primary {
    color: var(--accent);
    background: var(--accent-soft);
}

.tag.is-danger {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 15%, transparent);
}

.help {
    margin: 0;
    color: var(--faint);
    font-size: 11.5px;
}

.progress {
    display: block;
    width: 100%;
    height: 4px;
    margin: 6px 0 0;
    appearance: none;
    border: 0;
    border-radius: 2px;
    background: var(--raised);
    overflow: hidden;
}

.progress::-webkit-progress-bar {
    background: var(--raised);
    border-radius: 2px;
}

.progress::-webkit-progress-value {
    background: var(--muted);
    border-radius: 2px;
}

.progress::-moz-progress-bar {
    background: var(--muted);
    border-radius: 2px;
}

.progress.is-primary::-webkit-progress-value { background: var(--accent); }
.progress.is-success::-webkit-progress-value { background: var(--ok); }
.progress.is-info::-webkit-progress-value { background: var(--info); }
.progress.is-primary::-moz-progress-bar { background: var(--accent); }
.progress.is-success::-moz-progress-bar { background: var(--ok); }
.progress.is-info::-moz-progress-bar { background: var(--info); }

/* --- Floating notifications ---------------------------------------------- */

.notification {
    position: relative;
    margin: 14px 0;
    padding: 12px 38px 12px 14px;
    color: var(--text);
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-left: 3px solid var(--muted);
    border-radius: var(--radius-sm);
    font-size: 13px;
    animation: slide-in 0.18s ease-out;
}

@keyframes slide-in {
    from {
        opacity: 0;
        transform: translateY(-6px);
    }
}

/* Those created by script.js are fixed to the corner and need their own
   background, since they sit over the stage. */
body > .notification {
    margin: 0;
    background: var(--panel);
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
}

.notification.is-warning {
    border-left-color: var(--warn);
}

.notification.is-success {
    border-left-color: var(--ok);
}

.notification.is-danger {
    border-left-color: var(--danger);
}

.notification.is-info {
    border-left-color: var(--info);
}

.notification strong {
    font-size: 13px;
}

/* The dismiss control script.js writes as a button with class delete. */
.delete {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 26px;
    height: 26px;
    color: var(--faint);
    background: none;
    border: 0;
    border-radius: 6px;
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
}

.delete:hover {
    color: var(--text);
    background: var(--raised);
}

/* The offline banner sits in the stage rather than floating over it. */
.banner {
    position: relative;
    margin-top: 18px;
    padding: 12px 40px 12px 14px;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-left: 3px solid var(--warn);
    border-radius: var(--radius-sm);
    font-size: 13px;
}

.banner p {
    margin: 0;
}

.banner p + p {
    margin-top: 4px;
    color: var(--muted);
}

/* --- Notice ---------------------------------------------------------------
   Said in the transport rather than as a toast: the transport is fixed to the
   bottom, so a message about playback cannot scroll away, and it holds until it
   is dismissed. See the ended handler in script.js. */

.notice {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin: 2px 0 8px;
    padding: 10px 12px;
    background: var(--panel-2);
    border: 1px solid var(--line);
    border-left: 3px solid var(--warn);
    border-radius: var(--radius-sm);
}

.notice__text {
    flex: 1 1 auto;
    min-width: 0;
}

.notice__title {
    margin: 0;
    font-weight: 600;
    font-size: 13px;
}

.notice__body {
    margin: 3px 0 0;
    color: var(--muted);
    font-size: 12.5px;
    line-height: 1.4;
}

.notice__close {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--muted);
    font-size: 18px;
    line-height: 1;
    cursor: pointer;
}

.notice__close:hover {
    background: var(--raised);
    color: var(--text);
}

/* --- Skeletons ------------------------------------------------------------ */

.skeleton {
    height: 62px;
    margin-bottom: 6px;
    border-radius: var(--radius-sm);
    background: linear-gradient(90deg, var(--panel-2) 25%, var(--raised) 50%, var(--panel-2) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.3s infinite;
}

.skeleton--short {
    height: 42px;
}

@keyframes shimmer {
    to {
        background-position: -200% 0;
    }
}

/* --- Utility classes that script.js emits --------------------------------- */

.is-flex { display: flex; }
.is-flex-grow-1 { flex: 1 1 auto; min-width: 0; }
.is-justify-content-space-between { justify-content: space-between; }
.is-align-items-center { align-items: center; }
.is-flex-wrap-wrap { flex-wrap: wrap; }
.is-clickable { cursor: pointer; }
.is-size-6 { font-size: 13px; }
.is-size-7 { font-size: 11.5px; }
.has-text-grey { color: var(--faint); }
.has-text-grey-dark { color: var(--muted); }
.has-text-danger { color: var(--danger); }
.mb-0 { margin-bottom: 0 !important; }
.mb-1 { margin-bottom: 3px; }
.mb-2 { margin-bottom: 6px; }
.mb-4 { margin-bottom: 10px; }
.mt-1 { margin-top: 3px; }
.mt-2 { margin-top: 6px; }
.ml-2 { margin-left: 6px; }
.ml-auto { margin-left: auto; }
.p-3 { padding: 9px 10px; }

/* ==========================================================================
   Small screens: the rail becomes a drawer.
   ========================================================================== */

.scrim {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 20;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(1.5px);
}

@media (max-width: 899px) {
    .app {
        grid-template-columns: minmax(0, 1fr);
        grid-template-rows: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "top"
            "stage"
            "transport";
    }

    .topbar__nav {
        display: grid;
    }

    .rail {
        position: fixed;
        top: 56px;
        bottom: 0;
        left: 0;
        z-index: 30;
        width: min(88vw, 360px);
        border-right: 1px solid var(--line);
        transform: translateX(-100%);
        transition: transform 0.22s cubic-bezier(0.2, 0, 0, 1);
        box-shadow: 24px 0 60px rgba(0, 0, 0, 0.4);
    }

    body.nav-open .rail {
        transform: none;
    }

    body.nav-open .scrim {
        display: block;
    }

    .stage {
        padding: 16px 14px 20px;
    }

    /* Touch targets get bigger and the cover smaller, so the first row of the
       hero plus two chapters still fit above the transport on a phone. */
    .hero {
        align-items: center;
        gap: 14px;
        margin-bottom: 18px;
    }

    .hero__cover {
        width: 84px;
        height: 84px;
        border-radius: 8px;
    }

    .hero__title {
        font-size: 21px;
    }

    .hero__author {
        margin-top: 4px;
        font-size: 13px;
    }

    .hero__actions {
        margin-top: 8px;
    }

    .btn {
        min-height: 34px;
        font-size: 12.5px;
    }

    .panel-block {
        min-height: 52px;
        padding: 0 10px;
        gap: 10px;
    }

    .panel-block > span:nth-of-type(2) {
        font-size: 13px;
    }

    .transport {
        padding: 6px 10px calc(8px + env(safe-area-inset-bottom));
    }

    .transport__scrub {
        gap: 10px;
    }

    .time {
        min-width: 92px;
        font-size: 10.5px;
    }

    .transport__row {
        gap: 8px;
    }

    /* The book line is dropped on a phone: the chapter title and the hero above
       already say which book this is, and the row is short of width. */
    .transport__book {
        display: none;
    }

    .transport__actions {
        gap: 0;
    }

    .btn-icon {
        width: 40px;
        height: 40px;
    }

    .btn-play {
        width: 48px;
        height: 48px;
        margin: 0 2px;
    }
}

@media (max-width: 420px) {
    /* On the narrowest phones the seek buttons go: the scrubber and the
       play control are what get used, and five controls plus a title do not
       fit without shrinking every target below a thumb. */
    #btn-back,
    #btn-fwd {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        transition-duration: 0.001ms !important;
    }
}

/* ==========================================================================
   Standalone pages: sign-in and signed-out.

   body.page is a centred card on the app's own background, so the door and the
   room behind it look like the same building.
   ========================================================================== */

body.page {
    display: grid;
    place-items: center;
    padding: 24px;
    overflow: auto;
}

.page__card {
    width: min(420px, 100%);
    padding: 32px 28px 26px;
    text-align: center;
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: 16px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
}

.page__mark {
    width: 52px;
    height: 52px;
    border-radius: 12px;
}

.page__title {
    margin: 14px 0 4px;
    font-family: var(--serif);
    font-size: 27px;
    font-weight: 500;
    letter-spacing: -0.01em;
}

.page__subtitle {
    margin: 0 0 22px;
    color: var(--muted);
    font-size: 14px;
}

.page__button {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    width: 100%;
    min-height: 46px;
    color: var(--accent-ink);
    background: var(--accent);
    border-radius: 10px;
    font-size: 14.5px;
    font-weight: 600;
    text-decoration: none;
}

.page__button:hover {
    filter: brightness(1.07);
}

.page__button svg {
    width: 18px;
    height: 18px;
}

.page__foot {
    margin: 20px 0 0;
    color: var(--faint);
    font-size: 11.5px;
    line-height: 1.5;
}

body.page .notification {
    margin: 0 0 18px;
    text-align: left;
}

/* Sizes script.js and the pages ask for that the base styles do not set. */
.progress.is-small {
    height: 3px;
}

.button.is-small {
    min-width: 26px;
    min-height: 26px;
    font-size: 11px;
}

.button.is-medium {
    min-height: 34px;
}

.icon.is-medium {
    font-size: 16px;
}

.is-small {
    font-size: 12px;
}
