* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; background: #000; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }

/* Reader text is presentation, not an article: no selection, no long-press
   callout, no blue highlight when tapping to advance. */
#app, .back {
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
}

/* Phones with a home-bar/notch need their own inset and nothing more. The old
   18vh lift here double-counted the URL bar: 100dvh already shrinks when the
   browser chrome shows, so the box sat a whole toolbar too high on real phones. */
:root { --vn-lift: env(safe-area-inset-bottom, 0px); }

#app {
    position: relative;
    width: 100%;
    height: 100dvh;
    overflow: hidden;
    opacity: 1;
    transition: opacity .28s ease;
}
#app.in, #app.out { opacity: 0; }

/* Minimal exit: one small label, no chrome, fades out once the scene starts.
   Sits above the safe area so the notch/URL bar never clips it. */
.back {
    position: fixed;
    top: calc(10px + env(safe-area-inset-top));
    left: calc(12px + env(safe-area-inset-left));
    z-index: 5;
    padding: 6px 10px;
    border-radius: 999px;
    color: rgba(255, 255, 255, .72);
    background: rgba(0, 0, 0, .38);
    font-size: 12px;
    letter-spacing: .02em;
    text-decoration: none;
    backdrop-filter: blur(6px);
    transition: opacity .3s, color .15s;
}
.back:hover, .back:focus-visible { color: #ffd36e; }
/* Once the choices are up the reader is committed; get out of the way. */
#app.end + .back { opacity: .35; }

.bg {
    position: absolute;
    inset: 0;
    background-color: #101018;
}
/* Portrait art on a landscape screen: cover throws away about 60% of a 3:4
   picture, so on wide viewports the art is letterboxed whole and a blurred
   copy fills the sides. Phones stay on cover. */
.bg::before, .bg::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--art);
    background-position: center;
}
.bg::before { background-size: cover; filter: blur(26px) brightness(.5) saturate(.85); }
.bg::after { background-size: contain; background-repeat: no-repeat; }
@media (max-aspect-ratio: 3/4) {
    .bg::before { display: none; }
    .bg::after { background-size: cover; }
}
.shade {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0, 0, 0, .88), rgba(0, 0, 0, 0) 60%);
}

.box {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    max-width: 900px;
    margin: 0 auto;
    padding: 4vh min(7vw, 64px) calc(min(7vh, 56px) + var(--vn-lift));
}

.line {
    margin: .55em 0;
    color: #fff;
    font-size: calc(clamp(16px, 2.2vw, 22px) * var(--vn-fs, 1));
    line-height: 1.55;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .9);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .25s, transform .25s;
    /* Tapping to advance must not select a line or drag a highlight across the art. */
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}
.line.on { opacity: 1; transform: none; }
.line .who { color: #ffd36e; margin-right: .5em; }
.line.narr { color: #e6e6e6; font-style: italic; }

.choices {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 1.4em;
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s;
}
#app.end .choices { opacity: 1; pointer-events: auto; }

.choices a {
    color: #fff;
    text-decoration: none;
    background: rgba(20, 20, 30, .72);
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 8px;
    padding: .7em 1em;
    font-size: calc(clamp(15px, 2vw, 19px) * var(--vn-fs, 1));
    backdrop-filter: blur(4px);
    transition: background .15s, border-color .15s;
}
.choices a:hover { background: rgba(255, 211, 110, .22); border-color: #ffd36e; }

/* Star rating on the last scene. Sits where the choices would, revealed by the
   same #app.end class, so it appears once the final line has been read. */
.rate {
    margin-top: 1.4em;
    opacity: 0;
    pointer-events: none;
    transition: opacity .25s;
}
#app.end .rate { opacity: 1; pointer-events: auto; }
.rate fieldset { border: 0; margin: 0; padding: 0; display: flex; align-items: center; gap: 4px; }
.rate legend {
    float: left;
    margin-right: 12px;
    padding: 0;
    color: rgba(255, 255, 255, .72);
    font-size: 13px;
}
.rate button {
    padding: 2px 4px;
    border: 0;
    background: none;
    color: rgba(255, 255, 255, .28);
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
    transition: color .15s, transform .15s;
}
/* Hovering a star fills every star up to it, not just the one under the pointer. */
.rate fieldset:has(button:nth-of-type(1):hover) button:nth-of-type(1),
.rate fieldset:has(button:nth-of-type(2):hover) button:nth-of-type(-n+2),
.rate fieldset:has(button:nth-of-type(3):hover) button:nth-of-type(-n+3),
.rate fieldset:has(button:nth-of-type(4):hover) button:nth-of-type(-n+4),
.rate fieldset:has(button:nth-of-type(5):hover) button:nth-of-type(-n+5) { color: #ffd36e; }
.rate button:hover,
.rate button:focus-visible { color: #ffd36e; transform: scale(1.12); }
.rate button:disabled { opacity: .5; cursor: default; }

/* Short screens: tighter type so a five line scene plus two choices still fits. */
@media (max-height: 700px) {
    .box { padding-top: 2vh; }
    .line { margin: .4em 0; line-height: 1.45; }
    .choices { margin-top: .9em; gap: 8px; }
    .choices a { padding: .6em .9em; }
}

@media (max-width: 600px) {
    .box { padding-left: 18px; padding-right: 18px; }
    .line { font-size: 17px; }
}
