/*
 * "clouds" theme -- ported from https://clouds.wu-99.com/ (source at
 * /home/wukong/clouds.wu-99.com), generalized from that one-book landing
 * page into a reusable Django theme: same color palette, hero card, and
 * section-card language, but every book-specific string (title, author,
 * chapter list) comes from template context instead of being baked in.
 *
 * BOOK_THEME=clouds selects this file (see bookproject/settings.py and
 * templates/base.html). Adding another theme means dropping a new
 * <name>/theme.css next to this one and pointing BOOK_THEME at it -- no
 * Python changes needed.
 */

:root {
    color-scheme: light;
    --bg: #f6f8fc;
    --card: #ffffff;
    --text: #1f2937;
    --muted: #6b7280;
    --primary: #2563eb;
    --primary-hover: #1d4ed8;
    --border: #dbe3f0;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC",
        "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
    background: linear-gradient(180deg, #eff5ff 0%, var(--bg) 60%);
    color: var(--text);
    line-height: 1.65;
    /* .site-nav's full-bleed breakout below uses the standard `100vw`
       trick, which (unlike `100%`) doesn't subtract the vertical
       scrollbar's own width -- so it overshoots by a few px and would
       otherwise add a horizontal scrollbar to the whole page. */
    overflow-x: hidden;
}

/* BookSettings.background -- the image itself comes in via an inline
   style="background-image:..." (per-book, so it can't live in this
   shared theme file); this just supplies how it's positioned/sized,
   which the inline style doesn't set and so doesn't override. */
body.has-bg {
    background-size: cover;
    background-position: center top;
    background-attachment: fixed;
    background-repeat: no-repeat;
}

.container {
    max-width: 760px;
    margin: 0 auto;
    padding: 32px 20px 56px;
}

/* Full viewport width, breaking out of .container's own 760px/padding the
   same way .wide-section does further down -- but uncapped (no max-width),
   since the nav is meant to read as a page-wide bar, not a wider column.
   The negative top margin cancels .container's own 32px top padding so
   the bar sits flush against the very top of the page; everything below
   it still flows through .container as before. */
.site-nav {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 10px 12px;
    font-size: 0.95rem;
    box-sizing: border-box;
    width: 100vw;
    position: relative;
    left: 50%;
    transform: translateX(-50%);
    margin: -32px 0 20px;
    padding: 14px 20px;
    background: var(--card);
    border-bottom: 1px solid var(--border);
}

/* Without this, a flex item shrinks below its content width by default
   and wraps its own text instead -- on a narrow viewport, with this many
   nav items (brand + praise/events + 5-language switcher + login/signup),
   that broke individual links mid-word ("云层" / "之上" on separate
   lines) rather than letting whole items flow onto their own row. */
.site-nav .brand,
.site-nav .links,
.site-nav .links > * {
    white-space: nowrap;
}

/* The language switcher wraps as a block (its 5 links won't all fit on
   one line next to everything else on a narrow viewport), but each
   individual language link/label stays intact. */
.site-nav .lang-switch { white-space: normal; }
.site-nav .lang-switch a,
.site-nav .lang-switch .active { white-space: nowrap; }

/* A custom background photo sits directly behind the page's own content
   cards (nothing else does -- every other section is inside an opaque
   card), so the nav's own bar just goes translucent + blurred instead of
   fully opaque, rather than changing shape/position -- it's already a
   full-width bar by default above. Only applies when BookSettings.background
   is actually set (see body.has-bg in base.html) -- a book without one
   keeps the plain opaque nav, unchanged. */
body.has-bg .site-nav {
    background: rgba(255, 255, 255, 0.85);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: 0 4px 16px rgba(15, 23, 42, 0.12);
}

.site-nav .brand {
    color: var(--text);
    text-decoration: none;
    font-weight: 700;
    font-size: 1.05rem;
}

.site-nav .brand-logo { display: inline-flex; align-items: center; }
.site-nav .brand-logo img { display: block; height: 28px; width: auto; max-width: 160px; object-fit: contain; }

.site-nav .links { display: flex; align-items: center; gap: 14px; }

.user-badge { display: inline-flex; align-items: center; gap: 6px; }

.user-avatar {
    height: 22px;
    width: 22px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.user-avatar-initials {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--primary);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 600;
}

.user-avatar-lg { height: 64px; width: 64px; font-size: 1.3rem; }

.profile-avatar-preview { margin-bottom: 14px; }

.site-nav a { color: var(--muted); text-decoration: none; }
.site-nav a:hover { text-decoration: underline; }

/* Reader Praise / Events -- deliberately not styled like the rest of the
   nav's plain muted text links (language switcher, login/signup), since
   those are content pages, not chrome, and easy to miss otherwise. A
   filled pill in the primary color instead of an underline on hover. */
.site-nav a.nav-pill {
    color: #fff;
    background: var(--primary);
    padding: 5px 13px;
    border-radius: 999px;
    font-weight: 600;
    font-size: 0.85rem;
}

.site-nav a.nav-pill:hover {
    background: var(--primary-hover);
    text-decoration: none;
}

.lang-switch { font-size: 0.9rem; color: var(--muted); }
.lang-switch a { color: var(--primary); text-decoration: none; }
.lang-switch a:hover { text-decoration: underline; }
.lang-switch .active { color: var(--text); font-weight: 600; }

.site-nav form { display: inline; margin: 0; }
.site-nav button.link-button {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--muted);
    cursor: pointer;
}
.site-nav button.link-button:hover { text-decoration: underline; }

h1, h2, h3 { margin: 0; }

.messages { list-style: none; padding: 0; margin: 0 0 18px; }
.messages li {
    padding: 10px 14px;
    margin-bottom: 8px;
    border-radius: 8px;
    background: var(--card);
    border: 1px solid var(--border);
    font-size: 0.95rem;
}

.hero {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 28px 24px 30px;
    box-shadow: 0 10px 30px rgba(37, 99, 235, 0.08);
}

.hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(160px, 220px);
    gap: 20px;
    align-items: start;
}

.hero h1 {
    font-size: clamp(1.7rem, 4vw, 2.4rem);
    margin-bottom: 6px;
}

.hero .subtitle {
    font-size: 1.05rem;
    color: var(--muted);
    margin: 0 0 14px;
}

.hero .tagline {
    margin: 0;
    font-size: 1rem;
    max-width: 38em;
}

/* Buy links live inside the hero card now, not their own separate
   section/card below -- one fewer box on the page for the same content. */
.hero-buy {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}

.buy-label {
    margin: 0 0 8px;
    font-weight: 600;
    color: var(--muted);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.pitch-lines {
    margin: 14px 0 0;
    padding: 0;
    list-style: none;
}

.pitch-lines li {
    margin: 0 0 8px;
    padding-left: 1.1em;
    position: relative;
}

.pitch-lines li::before {
    content: "—";
    position: absolute;
    left: 0;
    color: var(--muted);
}

.meta-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 10px 16px;
    margin-top: 20px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
    font-size: 0.9rem;
}

.meta-grid dt {
    margin: 0;
    font-weight: 600;
    color: var(--muted);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.meta-grid dd { margin: 4px 0 0; }

.hero-visual { display: flex; flex-direction: column; gap: 10px; }

.cover-zoom {
    display: block;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
    font: inherit;
    text-align: inherit;
    width: 100%;
    cursor: zoom-in;
}

.hero-cover-link {
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid var(--border);
    box-shadow: 0 4px 16px rgba(37, 99, 235, 0.12);
}

.hero-cover-link img { display: block; width: 100%; height: auto; }

.hero-3d {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
}

.hero-3d .cover-zoom {
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid var(--border);
    box-shadow: 0 4px 16px rgba(37, 99, 235, 0.1);
    background: var(--card);
}

.hero-3d img { display: block; width: 100%; height: auto; }

/* Click-to-zoom modal, triggered by any .cover-zoom button (see
   templates/chapters/home.html's inline script) -- one shared lightbox
   for the main cover and the front/back thumbnails alike. */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(7, 12, 22, 0.9);
    cursor: zoom-out;
}

.lightbox.open { display: flex; }

.lightbox img {
    max-width: 96vw;
    max-height: 96vh;
    object-fit: contain;
    border-radius: 10px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
}

/* chapters/home.html's promo-popup -- the nearest upcoming BookEvent's
   flyer image, floated in a corner and linking through to the full
   events page. Dismissible (its own inline script removes it and
   remembers the choice in localStorage, keyed by that event's pk, so
   dismissing one flyer doesn't hide a later, different event's).
   `clamp()` keeps it comfortably readable on a real desktop window
   (~240px) without letting a vw-based width balloon on a very wide
   screen or shrink to near-illegible on a narrow one (floors at 170px,
   still 130px on a small phone via the media query below) -- a flyer/
   poster upload can be arbitrarily large (portrait, a few MB), and this
   is a corner nudge, not a focused view -- height is left to `auto` off
   that width so the whole flyer stays visible (uncropped) rather than
   object-fit-cropping a poster's own text/QR code. The gentle bob
   (`animation` below) is what actually makes it read as "floating"
   rather than just corner-pinned. */
.promo-popup {
    position: fixed;
    right: 20px;
    bottom: 20px;
    z-index: 500;
    width: clamp(170px, 20vw, 240px);
    border-radius: 12px;
    overflow: hidden;
    background: var(--card);
    border: 1px solid var(--border);
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.25);
    animation: promo-popup-float 3.2s ease-in-out infinite;
}

@keyframes promo-popup-float {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-10px); }
}

@media (prefers-reduced-motion: reduce) {
    .promo-popup { animation: none; }
}

@media (max-width: 480px) {
    .promo-popup { width: 130px; right: 12px; bottom: 12px; }
}

.promo-popup a { display: block; }

.promo-popup img {
    display: block;
    width: 100%;
    height: auto;
}

.promo-popup-close {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 1;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    border: none;
    background: rgba(15, 23, 42, 0.65);
    color: #fff;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.promo-popup-close:hover { background: rgba(15, 23, 42, 0.85); }

.section {
    margin-top: 22px;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 22px 24px;
}

.section h2 { font-size: 1.15rem; margin-bottom: 14px; }

.chapter-list { list-style: none; margin: 0; padding: 0; }

.chapter-list li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    padding: 11px 24px;
    margin: 0 -24px;
    border-radius: 8px;
    border-bottom: 1px solid var(--border);
    transition: background-color 0.15s ease;
}

.chapter-list li:last-child { border-bottom: none; }

.chapter-list li:hover { background: var(--bg); }

.chapter-list a {
    color: var(--text);
    text-decoration: none;
    font-weight: 500;
}

.chapter-list li:hover a { color: var(--primary); text-decoration: underline; }

/* Every card on the home page breaks out wider than .container (760px)
   together -- only when there's actually room past the ~1080px breakpoint
   below, and only when the chapter list has a preview panel to fill --
   so the hero/chapters/buy-links cards all stay the same width as each
   other (this class goes on all three), instead of just the one card
   with a panel ending up oddly wider than its neighbors. */
.wide-section {
    width: 100vw;
    max-width: 1040px;
    position: relative;
    left: 50%;
    transform: translateX(-50%);
}

@media (max-width: 1080px) {
    .wide-section {
        width: 100%;
        max-width: none;
        left: auto;
        transform: none;
    }
}

.chapters-layout { display: flex; align-items: flex-start; gap: 28px; }

/* Sized to what chapter titles actually need, not "however much space is
   left" -- the preview panel gets everything else instead of a fixed
   width, so it grows to fill the section's own extra room. */
.chapters-layout .chapter-list { flex: 0 1 300px; min-width: 0; }

/* All previews stacked in the same spot; :has() reveals the one matching
   the hovered row (see the per-chapter rules the template generates) --
   no JS needed to swap which image shows. */
.chapter-preview-panel {
    position: relative;
    flex: 1 1 auto;
    aspect-ratio: 3 / 4;
    border-radius: 14px;
    overflow: hidden;
    background: var(--bg);
    border: 1px solid var(--border);
    box-shadow: 0 16px 40px rgba(15, 23, 42, 0.16);
}

.chapter-preview-panel img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.2s ease;
}

/* The book's own cover, shown while nothing's hovered -- painted first
   (DOM order) so any chapter preview that fades in above it, on hover,
   covers it rather than the other way round. */
.chapter-preview-panel .chapter-preview-default { opacity: 1; }

@media (max-width: 720px) {
    .chapter-preview-panel { display: none; }
}

/* Locked chapter preview -- the head/tail paragraphs chapters.views'
   chapter_preview_blocks() picks, shown either side of the password
   form with a fade into the hidden middle (mask-image, not a solid
   cutoff) so it visually reads as "more chapter continues here" rather
   than as its own separate excerpt. */
.chapter-text-preview {
    line-height: 1.75;
    color: var(--text);
}

.chapter-text-preview p,
.chapter-text-preview h1,
.chapter-text-preview h2,
.chapter-text-preview h3,
.chapter-text-preview h4 {
    margin: 0 0 16px;
}

.chapter-text-preview-head {
    max-height: 320px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
    mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
}

.chapter-text-preview-tail {
    max-height: 220px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to top, #000 55%, transparent 100%);
    mask-image: linear-gradient(to top, #000 55%, transparent 100%);
}

.chapter-lock-gate {
    margin: 28px 0;
    padding: 22px 0;
    text-align: center;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}

/* Overrides the site-wide stacked-field form styling (`form label`/`form
   input` above, display:block + width:100%, meant for the taller login/
   signup/profile forms) -- password + unlock button read better as one
   compact row here than as three stacked full-width lines. */
.chapter-lock-gate form {
    margin-top: 10px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.chapter-lock-gate form label { display: inline; margin-bottom: 0; }

/* Not scoped to input[type="password"] -- the browser's own reveal-
   password toggle flips this field's type to "text" in place, and this
   is the only input .chapter-lock-gate's form ever has, so there's
   nothing else here for a type-independent selector to accidentally
   catch. Losing this rule the moment the type flips (the previous,
   type-scoped selector's bug) is what made the field suddenly snap back
   to the site-wide stacked-form width:100% mid-edit. */
.chapter-lock-gate form input { width: auto; flex: 0 1 200px; }

.btn {
    display: inline-block;
    text-decoration: none;
    padding: 2px 4px;
    border-radius: 6px;
    border: 1px solid var(--primary);
    color: var(--primary);
    background: transparent;
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
    transition: 0.2s ease;
}

.btn.primary { background: var(--primary); color: #fff; }

.btn:hover { transform: translateY(-1px); border-color: var(--primary-hover); color: var(--primary-hover); }
.btn.primary:hover { background: var(--primary-hover); color: #fff; }

/* templates/accounts/login.html & signup.html -- a full-width button
   above the email/password form (not a small link squeezed in next to
   "Sign up" below it), Google's own icon instead of plain text, matching
   the look of this project's other Google-login pages (distaisys.com). */
.btn-google {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    box-sizing: border-box;
    padding: 11px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text);
    font-weight: 600;
    font-size: 0.95rem;
    text-decoration: none;
    transition: 0.2s ease;
}
.btn-google:hover { border-color: var(--muted); box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08); }
.btn-google-icon { width: 18px; height: 18px; flex-shrink: 0; }

.auth-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 18px 0;
    color: var(--muted);
    font-size: 0.85rem;
}
.auth-divider::before,
.auth-divider::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--border);
}

.buy-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-top: 6px;
}

.buy-platform {
    display: flex;
    flex-direction: column;
    align-items: center;
    font-size: 0.72rem;
    line-height: 1.3;
    color: var(--muted);
}

.buy-platform-icon {
    height: 18px;
    width: auto;
    max-width: 26px;
    object-fit: contain;
    margin-bottom: 2px;
}

/* Visually separate each platform's own cluster of buttons when they're
   all flowing on one line together, not each on its own row. */
.buy-platform:not(:first-child) {
    margin-left: 10px;
    padding-left: 14px;
    border-left: 1px solid var(--border);
}

.buy-note {
    margin-top: 12px;
    font-size: 0.9rem;
    color: var(--muted);
}

form p { margin: 0 0 14px; }
form label { display: block; margin-bottom: 5px; font-size: 0.9rem; color: var(--muted); }
form input, form textarea, form select {
    width: 100%;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 8px;
    font: inherit;
    box-sizing: border-box;
}
form input:focus, form textarea:focus, form select:focus {
    outline: none;
    border-color: var(--primary);
}
.errorlist { color: #b91c1c; font-size: 0.85rem; padding-left: 0; list-style: none; margin: 4px 0; }

.helptext, .muted { color: var(--muted); font-size: 0.9rem; }

.auth-actions { margin-top: 14px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

footer.site-footer {
    margin-top: 32px;
    font-size: 0.88rem;
    color: var(--muted);
    text-align: center;
}

/* Same reasoning as body.has-bg .site-nav above -- the footer also sits
   directly on the page background, not inside a card. */
body.has-bg footer.site-footer {
    background: rgba(255, 255, 255, 0.85);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border-radius: 12px;
    padding: 10px 16px;
}

/* chapters/praise.html -- clouds.wu-99.com-style reader-recommendation
   cards. */
.praise-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 16px;
}

.praise-card {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.praise-rating { color: #f59e0b; font-size: 0.95rem; letter-spacing: 1px; }

.praise-quote {
    margin: 0;
    font-size: 0.95rem;
    line-height: 1.6;
    color: var(--text);
}

.praise-author { display: flex; align-items: center; gap: 10px; margin-top: auto; }

.praise-avatar {
    height: 36px;
    width: 36px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.praise-author-name { font-weight: 600; font-size: 0.9rem; }
.praise-author-title { color: var(--muted); font-size: 0.82rem; }

/* chapters/events.html -- sjpl.bibliocommons.com-style event listing,
   split into upcoming (full card) and past (compact, no image/signup). */
.event-list { display: flex; flex-direction: column; gap: 16px; }

/* Stacked (poster on top, details below), not side-by-side -- .event-image
   below is wide enough now (960px) that a side-by-side column for the
   text stopped making sense next to it. The section itself gets
   .wide-section (see events.html) so there's actually room for it,
   same breakout .wide-section already uses on the home page. */
.event-card {
    display: flex;
    flex-direction: column;
    gap: 16px;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 16px;
    background: var(--bg);
}

/* Sized to actually read as a poster (most flyer uploads are portrait
   and text-heavy), not a thumbnail -- height:auto rather than a forced
   square + object-fit:cover, so nothing on the flyer gets cropped off. */
.event-image {
    width: 100%;
    max-width: 960px;
    height: auto;
    display: block;
    margin: 0 auto;
    border-radius: 8px;
}

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

.event-date { color: var(--muted); font-size: 0.82rem; }

.event-title { margin: 2px 0 6px; font-size: 1.05rem; }

.event-meta { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }

.event-type-badge {
    display: inline-block;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    background: var(--card);
    border: 1px solid var(--border);
    color: var(--muted);
}

.event-type-badge.event-type-online { color: var(--primary); border-color: var(--primary); }

.event-location { color: var(--muted); font-size: 0.85rem; }

.event-description { margin: 6px 0 10px; font-size: 0.92rem; }

.event-card-past { padding: 10px 16px; background: transparent; }
.event-card-past .event-title { font-size: 0.95rem; }
.event-list-past { gap: 6px; }
