/* BLACKGLASS landing page and fragment selection. */

body.landing {
  display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh;
  background:
    radial-gradient(ellipse 80% 55% at 50% 38%, rgba(159, 183, 170, 0.045), transparent 70%),
    var(--bg);
}

/* The header picture behind the wordmark (Jake, 2026-09-28): it fades out at the left and right
   edges and into the page at the bottom, by two masks intersected. It is out of the flow, so the
   page lays out as before; the header, main and footer sit above it. */
.landing-hero {
  position: absolute; top: 0; left: 0; right: 0; z-index: 0;
  height: clamp(330px, 38vw, 600px);
  /* A dark pool behind the wordmark, so the black glass and its pale edge read against the sky. */
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), url("/assets/img/header-main-old.jpg");
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), image-set(url("/assets/img/header-main-old.webp") type("image/webp"), url("/assets/img/header-main-old.jpg") type("image/jpeg"));
  background-size: 100% 100%, cover; background-position: center, center 42%; background-repeat: no-repeat;
  opacity: 0.85; pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 20%, #000 80%, transparent), linear-gradient(to bottom, #000 40%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(to right, transparent, #000 20%, #000 80%, transparent), linear-gradient(to bottom, #000 40%, transparent 100%);
  mask-composite: intersect;
}
/* The pixel-art header (BG-131; public/assets/art/landing-header.js, baked by tools/bake-landing-header.py):
   shown to players since 2026-10-01 (the class landing-hero--pixel on the hero, app/views/landing.php). The
   picture is enlarged pixel for pixel, so the browser keeps its pixels square when it scales it. */
.landing-hero--pixel {
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), url("/assets/img/header-main.png");
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), image-set(url("/assets/img/header-main.webp") type("image/webp"), url("/assets/img/header-main.png") type("image/png"));
  image-rendering: pixelated; opacity: 1;
  /* The picture dithers into the page at its own bottom and sides; the mask only softens the crop. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent), linear-gradient(to bottom, #000 58%, rgba(0, 0, 0, 0.8) 78%, transparent 100%);
  mask-image: linear-gradient(to right, transparent, #000 9%, #000 91%, transparent), linear-gradient(to bottom, #000 58%, rgba(0, 0, 0, 0.8) 78%, transparent 100%);
}
/* Phones show the middle two thirds of the picture: shift it a little so the walker on the left brow stays in. */
@media (max-width: 40rem) {
  .landing-hero--pixel { background-position: center, 34% 42%; }
}
/* The other times of day's baked files (the owner's preview, ?preview=header&time=…; BG-141). */
.landing-hero--day {
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), url("/assets/img/header-main-day.png");
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), image-set(url("/assets/img/header-main-day.webp") type("image/webp"), url("/assets/img/header-main-day.png") type("image/png"));
}
.landing-hero--night {
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), url("/assets/img/header-main-night.png");
  background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), image-set(url("/assets/img/header-main-night.webp") type("image/webp"), url("/assets/img/header-main-night.png") type("image/png"));
}
/* Phones get the spec's phone framing (270 × 360, composed for the tall screen: BG-141), baked beside the wide files. */
@media (max-width: 40rem) {
  .landing-hero--pixel {
    background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), url("/assets/img/header-main-phone.png");
    background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), image-set(url("/assets/img/header-main-phone.webp") type("image/webp"), url("/assets/img/header-main-phone.png") type("image/png"));
    background-position: center, center 42%;
  }
  .landing-hero--day {
    background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), url("/assets/img/header-main-day-phone.png");
    background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), image-set(url("/assets/img/header-main-day-phone.webp") type("image/webp"), url("/assets/img/header-main-day-phone.png") type("image/png"));
    background-position: center, center 42%;
  }
  .landing-hero--night {
    background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), url("/assets/img/header-main-night-phone.png");
    background-image: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent), image-set(url("/assets/img/header-main-night-phone.webp") type("image/webp"), url("/assets/img/header-main-night-phone.png") type("image/png"));
    background-position: center, center 42%;
  }
}
/* A version made in the picture system (BG-141; app/src/LandingHeader.php): its own 480 × 270 drawing as an
   image in the hero, cropped and placed as the baked picture is (cover, 42% down; 34% across on phones),
   enlarged pixel for pixel; the dark pool behind the wordmark is laid over it. */
.landing-hero--drawn { background-image: none; }
.landing-hero-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 42%; image-rendering: pixelated;
}
.landing-hero--drawn::after {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(ellipse 42% 26% at 50% 44%, rgba(10, 11, 12, 0.72), rgba(10, 11, 12, 0.25) 70%, transparent);
}
@media (max-width: 40rem) {
  .landing-hero-img { object-position: 34% 42%; }
  /* A drawing with its own phone framing (270 × 360, offered by the <picture>'s source) is composed for the
     phone: centred, not shifted to keep the walker in. */
  .landing-hero--phoned .landing-hero-img { object-position: 50% 42%; }
}
/* The header follows the visitor's clock (js/shell/landing-hero.js): the picture it leaves stays over the new
   one and fades out (not at all for readers who prefer reduced motion). */
.landing-hero-ghost { pointer-events: none; }
/* The owner's preview of the header (?preview=header): what is shown, and the other times of day. */
.landing-preview-note {
  position: fixed; left: 0.75rem; bottom: 0.75rem; z-index: 4; max-width: min(34rem, calc(100vw - 1.5rem));
  padding: 0.6rem 0.8rem; border: 1px solid var(--line); border-radius: var(--radius, 4px);
  background: var(--panel); color: var(--muted); font-family: var(--font-mono); font-size: 0.72rem; line-height: 1.5;
}
.landing-preview-note strong { color: var(--text-strong); }
.landing-preview-times { display: inline-flex; flex-wrap: wrap; gap: 0.25rem 0.75rem; margin-left: 0.25rem; }
.landing-preview-times a { color: var(--accent); }
.landing-preview-times a[aria-current="true"] { color: var(--text-strong); text-decoration: none; }
.landing-top, .landing-main, .landing-foot { position: relative; z-index: 1; }
/* The header above the main column: its ♪ volume popover (position: fixed, site.css) lives inside it,
   so at an equal level the main column, later in the page, covered the popover and took its clicks. */
.landing-top { z-index: 3; }
/* The wordmark reads over the picture: a soft shadow of the page colour behind it. */
.landing-hero ~ .landing-main .series-title .glass { filter: drop-shadow(0 0 18px rgba(10, 11, 12, 0.75)); }

.landing-top, .landing-foot {
  display: flex; justify-content: space-between; align-items: center;
  padding: 1rem 1.25rem; font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.1em; color: var(--faint);
}
.landing-top a, .landing-foot a { color: var(--muted); text-decoration: none; }
.landing-note a { color: var(--accent); }
.landing-top nav { display: flex; gap: 1.25rem; flex-wrap: wrap; justify-content: flex-end; }
.landing-top a:hover, .landing-foot a:hover { color: var(--text-strong); }
.landing-foot { text-transform: uppercase; letter-spacing: 0.3em; }
.landing-foot-links { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.5rem 1.5rem; }
/* Small phones: the footer's wide letter-spacing made the page scroll sideways; let it wrap. */
@media (max-width: 26rem) {
  .landing-foot { flex-wrap: wrap; gap: 0.75rem; letter-spacing: 0.18em; }
  .landing-foot-links { justify-content: flex-start; }
}

/* About dialog, opened from the About links (landing.js). */
dialog.about-dialog { width: min(36rem, calc(100vw - 1.5rem)); max-width: none; max-height: calc(100dvh - 1.5rem); overflow-y: auto; padding: 1.5rem 1.6rem 0.8rem; position: relative; }
.about-dialog h2 { font-family: var(--font-mono); font-size: 0.95rem; font-weight: 400; letter-spacing: 0.22em; text-transform: uppercase; color: var(--text-strong); margin: 0 2rem 1.1rem 0; }
.landing-about p { color: var(--text); font-size: 0.975rem; line-height: 1.65; margin: 0 0 0.95rem; }
.landing-about a { color: var(--accent); }

/* Support dialog: a carousel of Patreon tiers, opened from the Patreon links (landing.js). */
dialog.support-dialog { width: min(46rem, calc(100vw - 1.5rem)); max-width: none; max-height: calc(100dvh - 1.5rem); overflow-y: auto; padding: 1.5rem 1.5rem 1.1rem; position: relative; }
.support-dialog h2 { font-family: var(--font-mono); font-size: 0.95rem; letter-spacing: 0.22em; text-transform: uppercase; font-weight: 400; color: var(--text-strong); margin: 0 2rem 0.4rem 0; }
.support-lead { color: var(--muted); font-size: 0.9375rem; margin: 0 0 1rem; }
.support-close { position: absolute; top: 0.6rem; right: 0.7rem; background: none; border: 0; color: var(--muted); font-size: 1.6rem; line-height: 1; cursor: pointer; padding: 0.2rem 0.4rem; }
.support-close:hover { color: var(--text-strong); }
.support-carousel { position: relative; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius); }
.support-track { display: flex; transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.2, 1); touch-action: pan-y; }
.support-tier { flex: 0 0 100%; min-width: 0; display: flex; flex-direction: column; }
.support-tier picture, .support-tier img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
.support-body { padding: 1rem 1.1rem 1.1rem; display: flex; flex-direction: column; gap: 0.5rem; }
.support-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.support-head h3 { margin: 0; font-family: var(--font-mono); font-size: 1.05rem; letter-spacing: 0.2em; text-transform: uppercase; font-weight: 400; color: var(--text-strong); }
.support-head h3 .sb.support-tier-sb { font-size: 0.85rem; letter-spacing: 0.14em; }
.support-price { font-family: var(--font-mono); color: var(--accent); font-size: 1.05rem; white-space: nowrap; }
.support-price small { color: var(--faint); font-size: 0.7rem; letter-spacing: 0.08em; }
.support-tag { margin: 0; font-style: italic; color: var(--text); }
.support-body ul { margin: 0.1rem 0 0.4rem; padding-left: 1.1rem; color: var(--muted); font-size: 0.9rem; }
.support-body li { margin: 0.2rem 0; }
.support-join { align-self: flex-start; }
/* The arrows sit over the picture: an overlay with the image's 16:9 box, centred vertically. */
.support-navs { position: absolute; top: 0; left: 0; right: 0; aspect-ratio: 16 / 9; display: flex; align-items: center; justify-content: space-between; padding: 0 0.6rem; pointer-events: none; }
.support-nav {
  pointer-events: auto; width: 2.4rem; height: 2.6rem; border: 1px solid rgba(216, 212, 203, 0.25); border-radius: var(--radius);
  background: rgba(9, 10, 11, 0.6); color: var(--text-strong); font-size: 1.5rem; line-height: 1; cursor: pointer;
}
.support-nav:hover { background: rgba(9, 10, 11, 0.85); }
.support-nav:disabled { visibility: hidden; }
.support-dots { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.3rem 0.9rem; margin: 0.9rem 0 0.2rem; }
.support-dots button { background: none; border: 0; padding: 0.3rem 0; cursor: pointer; font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--faint); border-bottom: 1px solid transparent; }
.support-dots button[aria-current="true"] { color: var(--accent); border-bottom-color: currentColor; }
.support-dots button:hover { color: var(--text-strong); }
.support-foot { margin: 0.6rem 0 0; text-align: center; font-size: 0.8rem; color: var(--faint); }
.support-foot a { color: var(--muted); }
@media (max-width: 34rem) {
  dialog.support-dialog { padding: 1.1rem 0.9rem 0.9rem; }
  .support-nav { width: 2rem; height: 2.3rem; }
  .support-head { flex-direction: column; gap: 0.1rem; }
}
@media (prefers-reduced-motion: reduce) { .support-track { transition: none; } }

.landing-main { flex: 1; display: flex; flex-direction: column; align-items: center; padding: 6vh 1rem 3rem; }
.landing-notice { max-width: 34rem; margin-bottom: 2rem; }

.series-title { margin: 6vh 0 9vh; text-align: center; }
.series-title .glass {
  display: inline-block;
  font-family: var(--font-mono); font-weight: 400;
  font-size: clamp(2rem, 9.5vw, 5.6rem); letter-spacing: 0.32em; margin-right: -0.32em;
  color: #0d0f10;
  -webkit-text-stroke: 1px rgba(216, 212, 203, 0.55);
  text-shadow: 0 0 1px rgba(216, 212, 203, 0.25);
  background: linear-gradient(100deg, transparent 42%, rgba(200, 225, 212, 0.16) 50%, transparent 58%) no-repeat;
  background-size: 250% 100%; background-position: 120% 0;
  -webkit-background-clip: text; background-clip: text;
  animation: sheen 9s ease-in-out 1.2s infinite, arrive 2.4s ease-out both;
}
@keyframes sheen { 0%, 60% { background-position: 120% 0; } 100% { background-position: -120% 0; } }
@keyframes arrive { 0% { opacity: 0; } 30% { opacity: 0.2; } 34% { opacity: 0.8; } 36% { opacity: 0.3; } 100% { opacity: 1; } }
/* Occasional waking: stutter on, hold a breath of light, gutter out (triggered from landing.js). */
.series-title .glass.flicker { animation: sheen 9s ease-in-out 1.2s infinite, wake 2.8s linear both; }
@keyframes wake {
  0%, 100% { color: #0d0f10; text-shadow: 0 0 1px rgba(216, 212, 203, 0.25); }
  3% { color: #9fb8ac; text-shadow: 0 0 10px rgba(170, 220, 196, 0.5); }
  5% { color: #0d0f10; text-shadow: 0 0 1px rgba(216, 212, 203, 0.25); }
  9% { color: #7d948a; text-shadow: 0 0 6px rgba(170, 220, 196, 0.35); }
  10% { color: #0d0f10; }
  16%, 20% { color: #c9ded3; text-shadow: 0 0 14px rgba(170, 220, 196, 0.55), 0 0 36px rgba(120, 190, 160, 0.25); }
  21% { color: #3a4540; }
  23%, 52% { color: #d6e8de; text-shadow: 0 0 16px rgba(170, 220, 196, 0.6), 0 0 44px rgba(120, 190, 160, 0.3); }
  60% { color: #8fa69b; text-shadow: 0 0 8px rgba(170, 220, 196, 0.35); }
  62% { color: #1c2320; text-shadow: 0 0 2px rgba(170, 220, 196, 0.2); }
  66% { color: #6f8279; text-shadow: 0 0 6px rgba(170, 220, 196, 0.3); }
  70% { color: #0d0f10; }
  74% { color: #3d4944; text-shadow: 0 0 4px rgba(170, 220, 196, 0.2); }
  80% { color: #0d0f10; text-shadow: 0 0 1px rgba(216, 212, 203, 0.25); }
}

.fragments { list-style: none; margin: 0; padding: 0; width: min(34rem, 100%); border-top: 1px solid var(--line); }
.fragment {
  display: grid; grid-template-columns: 1fr auto; grid-template-areas: "num actions" "title actions" "state actions";
  gap: 0.15rem 1rem; align-items: center;
  padding: 1.4rem 0.25rem; border-bottom: 1px solid var(--line);
}
.fragment-num { grid-area: num; font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.3em; text-transform: uppercase; color: var(--muted); }
.fragment-title { grid-area: title; font-family: var(--font-mono); font-size: 1.25rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-strong); }
.fragment-beta {
  display: inline-block; vertical-align: middle; margin-left: 0.4rem; padding: 0.05rem 0.4rem;
  font-size: 0.625rem; letter-spacing: 0.12em; color: var(--warn); border: 1px solid var(--warn);
  border-radius: 2px; cursor: help;
}
.fragment-state { grid-area: state; font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); }
.fragment-actions { grid-area: actions; display: flex; flex-direction: column; gap: 0.4rem; align-items: stretch; }
.fragment.is-locked .fragment-num { color: var(--faint); }
.fragment.is-locked .fragment-state { color: var(--faint); }
.fragment.is-locked { opacity: 0.8; }

/* More than one Journey on this Fragment (a different protagonist each): one row per Journey,
   spanning the full width, instead of the narrow "actions" column a single Continue button fits in. */
.fragment.has-journeys { grid-template-columns: 1fr; grid-template-areas: "num" "title" "state" "actions"; }
.fragment-journeys { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
/* Always a full-width line of its own, even where the actions become a wrapping row (phones). */
.fragment-actions > .fragment-journeys { flex: 1 1 100%; width: 100%; }
.fragment-journey {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  padding: 0.55rem 0.7rem; border: 1px solid var(--line); border-radius: 4px;
}
.fragment-journey-info { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1 1 auto; }
/* Every row's Continue is the same size, whatever the text beside it (Jake, 2026-09-28). */
/* More specific than the phone rule `.fragment-actions .btn { flex: 1 }`, which would stretch them. */
.fragment-actions .fragment-journey > .btn { flex: 0 0 8.5rem; width: 8.5rem; min-width: 8.5rem; max-width: 8.5rem; box-sizing: border-box; text-align: center; justify-content: center; }
.fragment-journey-who { font-family: var(--font-mono); font-size: 0.8125rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-strong); }
.fragment-journey-act { color: var(--accent); text-transform: none; letter-spacing: 0; }
.fragment-journey-where { font-size: 0.8125rem; color: var(--muted); }

/* The Continue button's fill (Jake, 2026-09-28): how far into this protagonist's Journey the
   current Act is (n of the total; Engine.currentAct()'s `n`/`of`), filling left to right behind
   the label without changing the button's size. `data-progress` carries the step, nearest 5%
   (landing.php and js/shell/landing.js round it), rather than a `--progress` custom property set
   through an inline style: the site's CSP forbids inline styles outside /_dev/ (App::securityHeaders()),
   so a literal per-element value cannot be set that way. No `data-progress` (an old save from
   before `of` existed) leaves the button unfilled. */
.fragment-progress { position: relative; isolation: isolate; overflow: hidden; }
.fragment-progress::before {
  content: ""; position: absolute; inset: 0; right: auto; z-index: -1; width: 0;
  background: var(--accent); opacity: 0.4; transition: width 0.4s ease;
}
.fragment-progress[data-progress="5"]::before { width: 5%; }
.fragment-progress[data-progress="10"]::before { width: 10%; }
.fragment-progress[data-progress="15"]::before { width: 15%; }
.fragment-progress[data-progress="20"]::before { width: 20%; }
.fragment-progress[data-progress="25"]::before { width: 25%; }
.fragment-progress[data-progress="30"]::before { width: 30%; }
.fragment-progress[data-progress="35"]::before { width: 35%; }
.fragment-progress[data-progress="40"]::before { width: 40%; }
.fragment-progress[data-progress="45"]::before { width: 45%; }
.fragment-progress[data-progress="50"]::before { width: 50%; }
.fragment-progress[data-progress="55"]::before { width: 55%; }
.fragment-progress[data-progress="60"]::before { width: 60%; }
.fragment-progress[data-progress="65"]::before { width: 65%; }
.fragment-progress[data-progress="70"]::before { width: 70%; }
.fragment-progress[data-progress="75"]::before { width: 75%; }
.fragment-progress[data-progress="80"]::before { width: 80%; }
.fragment-progress[data-progress="85"]::before { width: 85%; }
.fragment-progress[data-progress="90"]::before { width: 90%; }
.fragment-progress[data-progress="95"]::before { width: 95%; }
.fragment-progress[data-progress="100"]::before { width: 100%; }
/* A Journey already completed with that protagonist: fully filled, and it glows softly every few
   seconds so a finished Journey reads differently from one merely far along. Reduced motion
   (base.css) already flattens the pulse to nothing, leaving it solid. */
.fragment-progress.is-complete::before { width: 100%; opacity: 1; }
.fragment-progress.is-complete, .fragment-progress.is-complete:hover { color: #0d0f10; }
.fragment-progress.is-complete { animation: fragment-complete-pulse 7s ease-in-out infinite; }
@keyframes fragment-complete-pulse {
  0%, 90%, 100% { box-shadow: none; }
  95% { box-shadow: 0 0 10px 2px rgba(159, 183, 170, 0.65); }
}

/* A gated protagonist newly unlocked here (registry `requires`, e.g. Nia): this account has never
   begun a Journey with them on this Fragment — Journeys::landingFragments()'s `unlocked_protagonist`
   (Jake, 2026-09-28: "highlight that Nia's character is now unlocked ... border that playthrough
   option in gold that flashes every now and then"). A soft gold ring around the row that glows every
   few seconds, drawing the eye without changing the row's size; .journey.is-unlocked (game.css) is
   the same treatment on "Choose your Journey". Becomes a plain Journey row, with no highlight, once
   her Journey has been started (the row then comes from `journeys` like any other). */
.fragment-journey.is-unlocked { box-shadow: inset 0 0 0 1px rgba(212, 175, 55, 0.5); animation: fragment-unlocked-pulse 6s ease-in-out infinite; }
.fragment-journey-badge { color: #d4af37; text-transform: none; letter-spacing: 0.02em; }
@keyframes fragment-unlocked-pulse {
  0%, 85%, 100% { box-shadow: inset 0 0 0 1px rgba(212, 175, 55, 0.45); }
  92% { box-shadow: inset 0 0 0 1px rgba(212, 175, 55, 0.75), 0 0 12px 2px rgba(212, 175, 55, 0.4); }
}
@media (prefers-reduced-motion: reduce) {
  .fragment-journey.is-unlocked { animation: none; box-shadow: inset 0 0 0 1px rgba(212, 175, 55, 0.6); }
}

.landing-note { margin-top: 2.5rem; max-width: 34rem; text-align: center; font-size: 0.9375rem; color: var(--muted); }
.landing-support { margin-top: 1.25rem; max-width: 34rem; text-align: center; font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.08em; color: var(--faint); }
.landing-support a { color: var(--accent); text-decoration: none; border-bottom: 1px solid currentColor; }
.landing-support a:hover { color: var(--text-strong); }
/* "Install the app" line (phones and tablets only; landing.js shows it). */
.landing-install { margin-top: 0.9rem; max-width: 30rem; text-align: center; font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.08em; line-height: 1.7; color: var(--faint); }
.landing-install[hidden] { display: none; }
.landing-install a { color: var(--accent); text-decoration: none; border-bottom: 1px solid currentColor; }
.landing-install a:hover { color: var(--text-strong); }
.landing-install a[data-install-dismiss] { color: var(--muted); border-bottom-color: transparent; }

@media (max-width: 34rem) {
  .fragment { grid-template-columns: 1fr; grid-template-areas: "num" "title" "state" "actions"; }
  .fragment-actions { flex-direction: row; flex-wrap: wrap; margin-top: 0.8rem; }
  .fragment-actions .btn { flex: 1; }
  .series-title { margin: 4vh 0 6vh; }
}

/* A support tier without a picture (none ships for it): its badge, large, in the picture's 16:9 box.
   The badge itself is public/assets/css/supporter.css; the account's own badge sits in .landing-top. */
.support-art {
  display: flex; align-items: center; justify-content: center; width: 100%; aspect-ratio: 16 / 9;
  background: radial-gradient(90% 120% at 50% 40%, #1c1f22 0%, #0d0f10 70%);
}
.support-art .sb { font-size: 1.15rem; }
.landing-top nav { align-items: center; }

/* Shards (docs/SHARDS.md): short teasers between the Fragments (app/views/_shards.php). Lighter than
   a Fragment: an indented, more compact row, "Shard I" in place of "Fragment I", one short line, and
   a thin faceted sliver of glass down its left edge. A locked Shard dims like a locked Fragment. */
.shard {
  position: relative; display: flex; flex-direction: column; gap: 0.15rem;
  margin-left: 2rem; padding: 0.8rem 0.25rem 0.85rem 1.1rem; border-bottom: 1px solid var(--line);
}
.shard::before {
  content: ""; position: absolute; left: 0; top: 0.75rem; bottom: 0.8rem; width: 5px;
  background: linear-gradient(180deg, rgba(195, 216, 204, 0.75) 0%, rgba(159, 183, 170, 0.35) 45%, rgba(159, 183, 170, 0.08) 100%);
  clip-path: polygon(0 0, 100% 14%, 100% 100%, 0 82%);
}
.shard-num { font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.34em; text-transform: uppercase; color: var(--accent); }
.shard-title { font-family: var(--font-mono); font-size: 0.875rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-strong); }
.shard-title a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--accent); }
.shard-title a:hover { color: var(--accent-strong); }
.shard-line { font-size: 0.8125rem; line-height: 1.5; color: var(--muted); }
.shard-chip { font-size: 0.5625rem; cursor: default; }
.shard.is-locked { opacity: 0.75; }
.shard.is-locked .shard-num { color: var(--faint); }
.shard.is-locked .shard-title { color: var(--muted); }
/* A Shard that can be played (app/views/_shards.php): Play, Continue, or Revisit and Play again, under
   its title and never folded away; the finished mark sits beside the title. */
.shard-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; margin-top: 0.45rem; position: relative; z-index: 1; }
.shard-where { font-size: 0.8125rem; color: var(--muted); }
/* The latest milestone reached, beside the place (BG-127), in the Act's colour on a Journey's row. */
.shard-milestone { font-size: 0.8125rem; color: var(--accent); }
.shard-title .done-mark { margin-left: 0.35em; }
@media (max-width: 34rem) {
  .shard { margin-left: 0.9rem; padding-left: 0.95rem; }
}

/* ---- landing page music: the "Play with music?" prompt (js/shell/site-music.js) -------------
   The header's ♪ control and its volume popover are on every site page (app/views/_music-control.php),
   so their rules are in site.css. */
/* The "Play with music?" prompt: a small, tasteful line above the Fragments, not a modal wall. */
.landing-music-prompt {
  max-width: 26rem; margin: 0 0 1.6rem; padding: 0.7rem 1rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel); color: var(--text); font-size: 0.9375rem;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.6rem 1rem;
}
.landing-music-prompt[hidden] { display: none; }
.landing-music-prompt .row { display: flex; gap: 0.6rem; }

/* A finished Journey's mark, left of "Journey complete" (app/views/_done-shard.php). */
.done-shard { display: inline-block; vertical-align: -0.28em; margin-right: 0.4em; flex: none; }

/* Beside the shard: a small blackglass display reading JOURNEY COMPLETE (app/views/_journey-lcd.php).
   A few cells are unlit, two are scrambled and now and then swap glyph, and the whole screen
   stutters dark for a moment every so often, on a cycle of its own per Journey (classes lcd-f0..5
   and lcd-p0..4, chosen from a hash of the Journey id: no inline styles under the CSP). */
.done-mark { display: inline-flex; align-items: center; vertical-align: middle; }
.done-mark .done-shard { margin-right: 0; }
.done-note { color: var(--muted); }

/* The display is the control (Jake, 2026-09-29): where a "Revisit" button used to be, the same size
   as a row's Continue button (8.5rem by 2rem), reading JOURNEY COMPLETE on one line. Hovering,
   focusing or tapping it wakes two lit choices under it (js/shell/landing.js setupLcdMenus). */
.lcd-menu { position: relative; display: block; flex: 0 0 8.5rem; width: 8.5rem; height: 2rem; }
.journey-lcd {
  display: block; width: 100%; height: 100%; margin: 0; max-width: 100%; box-sizing: border-box; padding: 2px; border-radius: 4px; cursor: pointer;
  font: inherit; color: inherit; border: 0;
  background: linear-gradient(160deg, #3b4744, #1a201f 55%, #2c3634);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 1px 4px rgba(0, 0, 0, 0.6);
  transition: box-shadow 0.25s;
}
.journey-lcd:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lcd-menu.is-open .journey-lcd { box-shadow: 0 0 0 1px rgba(150, 214, 184, 0.45), 0 0 10px rgba(120, 190, 160, 0.3); }
.lcd-glass {
  position: relative; display: flex; align-items: center; justify-content: center; height: 100%; box-sizing: border-box; padding: 0 0.2rem; border-radius: 2px; overflow: hidden;
  background: radial-gradient(120% 140% at 30% 0%, #16211e 0%, #0a0f0e 60%, #060909 100%);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.7), inset 0 0 10px rgba(0, 0, 0, 0.8);
  font-family: var(--font-mono); font-size: 0.6875rem; line-height: 1; letter-spacing: 0.05em; text-transform: uppercase; white-space: nowrap;
  color: #c9ded3; text-shadow: 0 0 5px rgba(170, 220, 196, 0.6), 0 0 12px rgba(120, 190, 160, 0.3);
  animation: lcd-stutter-a 31s linear infinite both;
}
.lcd-glass::after { /* a glass sheen and a faint pixel grid over the lit text */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(115deg, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0) 38%),
    repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.14) 0 1px, transparent 1px 3px);
}
.lcd-word { display: inline-block; white-space: nowrap; }
.lcd-c { display: inline-block; position: relative; }
.lcd-dim { color: rgba(201, 222, 211, 0.24); text-shadow: none; }
.lcd-x { color: transparent; text-shadow: none; }
.lcd-x::before, .lcd-x::after { position: absolute; left: 0; top: 0; color: #c9ded3; text-shadow: 0 0 5px rgba(170, 220, 196, 0.6); }
.lcd-x::before { content: attr(data-a); animation: lcd-glyph-a 9s linear infinite; }
.lcd-x::after { content: attr(data-b); opacity: 0; animation: lcd-glyph-b 9s linear infinite; }
.lcd-x1::before, .lcd-x1::after { animation-duration: 13s; animation-delay: -4s; }
.lcd-x2::before, .lcd-x2::after { animation-duration: 11s; animation-delay: -7s; }
.lcd-x3::before, .lcd-x3::after { animation-duration: 17s; animation-delay: -10s; }
@keyframes lcd-glyph-a { 0%, 62%, 70%, 100% { opacity: 1; } 63%, 69% { opacity: 0; } 66%, 67% { opacity: 0; } }
@keyframes lcd-glyph-b { 0%, 62%, 70%, 100% { opacity: 0; } 63%, 65% { opacity: 1; } 66% { opacity: 0; } 67%, 69% { opacity: 1; } }
/* Failing glass: mostly steady, then a few quick blinks or one longer dark moment. */
@keyframes lcd-stutter-a {
  0%, 84% { opacity: 1; } 84.5% { opacity: 0.1; } 85.5% { opacity: 1; } 86.5% { opacity: 0.2; } 87.5% { opacity: 1; }
  88.5% { opacity: 0.05; } 89.5% { opacity: 0.8; } 90.5% { opacity: 0.15; } 92% { opacity: 1; } 100% { opacity: 1; }
}
@keyframes lcd-stutter-b {
  0%, 71% { opacity: 1; } 71.4% { opacity: 0.3; } 72.2% { opacity: 1; }
  93% { opacity: 1; } 93.4% { opacity: 0.15; } 96.5% { opacity: 0.05; } 97% { opacity: 0.6; } 97.4% { opacity: 0.1; } 98.5% { opacity: 1; } 100% { opacity: 1; }
}
.lcd-f0 .lcd-glass { animation-duration: 19s; }
.lcd-f1 .lcd-glass { animation: lcd-stutter-b 23s linear infinite; }
.lcd-f2 .lcd-glass { animation-duration: 29s; }
.lcd-f3 .lcd-glass { animation: lcd-stutter-b 37s linear infinite; }
.lcd-f4 .lcd-glass { animation-duration: 41s; }
.lcd-f5 .lcd-glass { animation: lcd-stutter-b 47s linear infinite; }
.lcd-p0 .lcd-glass { animation-delay: -3s; }
.lcd-p1 .lcd-glass { animation-delay: -11s; }
.lcd-p2 .lcd-glass { animation-delay: -17s; }
.lcd-p3 .lcd-glass { animation-delay: -5s; }
.lcd-p4 .lcd-glass { animation-delay: -23s; }
@media (prefers-reduced-motion: reduce) {
  .lcd-glass, .lcd-x::before, .lcd-x::after { animation: none !important; }
  .lcd-x::after { opacity: 0; }
}
/* The two choices the glass wakes: a second pane under the display, each choice lit like its text. */
.lcd-options {
  position: absolute; z-index: 20; top: calc(100% + 3px); right: 0; width: 100%; min-width: 8.5rem; box-sizing: border-box; padding: 2px; border-radius: 4px;
  display: flex; flex-direction: column; gap: 2px;
  background: linear-gradient(160deg, #3b4744, #1a201f 55%, #2c3634);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 4px 12px rgba(0, 0, 0, 0.7);
  animation: lcd-wake 0.18s ease-out;
}
.lcd-options[hidden] { display: none; }
@keyframes lcd-wake { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.lcd-opt {
  display: flex; align-items: center; justify-content: center; min-height: 2rem; padding: 0 0.3rem; border-radius: 2px; box-sizing: border-box;
  background: radial-gradient(120% 140% at 30% 0%, #16211e 0%, #0a0f0e 60%, #060909 100%);
  font-family: var(--font-mono); font-size: 0.6875rem; letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none; white-space: nowrap;
  color: #c9ded3; text-shadow: 0 0 5px rgba(170, 220, 196, 0.6), 0 0 12px rgba(120, 190, 160, 0.3);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.7), inset 0 0 10px rgba(0, 0, 0, 0.8);
}
.lcd-opt:hover, .lcd-opt:focus-visible { color: #effff6; background: radial-gradient(120% 140% at 30% 0%, #23372f 0%, #0f1a16 65%, #08100d 100%); text-shadow: 0 0 6px rgba(190, 240, 214, 0.9), 0 0 14px rgba(120, 220, 170, 0.55); outline: none; box-shadow: inset 0 0 0 1px rgba(150, 214, 184, 0.55), inset 0 0 10px rgba(0, 0, 0, 0.6); }
@media (prefers-reduced-motion: reduce) {
  .lcd-options { animation: none; }
}

/* The New Journey button of a visitor with no Journey yet: every so often (three flares in a
   26 s cycle, 8 to 9 s apart) its border flares like firelight, ember orange to gold with a soft
   outer glow, for about 1.5 s, then fades back. Starts a couple of seconds after the page
   arrives. Hover and focus keep their own look between and over the flares. */
@keyframes ember-flare {
  0%, 31%, 65% { border-color: var(--accent); box-shadow: 0 0 0 rgba(255, 140, 40, 0); }
  0.8%, 31.8%, 65.8% { border-color: #ff8a2a; box-shadow: 0 0 6px rgba(255, 120, 30, 0.55), 0 0 14px rgba(255, 90, 20, 0.25); }
  1.6%, 32.6%, 66.6% { border-color: #f0b040; box-shadow: 0 0 10px rgba(255, 170, 50, 0.7), 0 0 24px rgba(255, 100, 20, 0.35); }
  2.6%, 33.6%, 67.6% { border-color: #d9701e; box-shadow: 0 0 5px rgba(255, 110, 30, 0.4), 0 0 12px rgba(255, 80, 20, 0.2); }
  3.6%, 34.6%, 68.6% { border-color: #ffc45a; box-shadow: 0 0 12px rgba(255, 190, 70, 0.75), 0 0 28px rgba(255, 110, 25, 0.4); }
  4.8%, 35.8%, 69.8% { border-color: #e2802a; box-shadow: 0 0 7px rgba(255, 130, 40, 0.4), 0 0 16px rgba(255, 90, 20, 0.2); }
  5.8%, 36.8%, 70.8%, 100% { border-color: var(--accent); box-shadow: 0 0 0 rgba(255, 140, 40, 0); }
}
.btn-ember { animation: ember-flare 26s ease-in-out 2.6s infinite; }
@media (prefers-reduced-motion: reduce) {
  .btn-ember { animation: none; border-color: rgba(226, 128, 42, 0.55); box-shadow: 0 0 6px rgba(255, 120, 30, 0.15); }
}

/* ---- folding (Jake, 2026-09-29) ---------------------------------------------------------------
   An available Fragment (and a Shard with a line) shows just its header until it is opened: number,
   title, chip and a one-line status, with Continue kept beside it for a Journey in progress. The
   rows and actions are in `.fragment-body`, which opens by animating its grid track from 0fr to 1fr
   (js/shell/landing.js setupFold()). The header text stays as it was; a real button (.fragment-toggle,
   aria-expanded and aria-controls) lies under it and the script also treats a click on the text as a
   click on the button. `is-settled` (set once the opening has finished) lets the Journey display's
   choices drop out of the body without being clipped. */
.fragment-toggle {
  position: absolute; inset: 0; z-index: 0; margin: 0; padding: 0; border: 0; background: transparent; color: inherit; cursor: pointer; border-radius: 4px;
}
.fragment-toggle:focus-visible { outline: 1px solid var(--accent); outline-offset: 1px; }
.fragment-body { display: grid; grid-template-rows: 0fr; visibility: hidden; transition: grid-template-rows 0.28s ease, visibility 0s linear 0.28s; }
.fragment-body-inner { min-height: 0; overflow: hidden; }
.is-open > .fragment-body { grid-template-rows: 1fr; visibility: visible; transition: grid-template-rows 0.28s ease, visibility 0s; }
.is-open.is-settled > .fragment-body > .fragment-body-inner { overflow: visible; }
@media (prefers-reduced-motion: reduce) {
  .fragment-body, .is-open > .fragment-body { transition: none; }
}
.fragment.is-foldable { position: relative; grid-template-columns: 1fr auto; grid-template-areas: "num quick" "title quick" "state quick" "body body"; }
.fragment.is-foldable .fragment-toggle { grid-area: 1 / 1 / 4 / 3; }
.fragment.is-foldable .fragment-num, .fragment.is-foldable .fragment-title, .fragment.is-foldable .fragment-state { position: relative; z-index: 1; pointer-events: none; }
.fragment.is-foldable .fragment-beta, .fragment.is-foldable .fragment-all-complete { pointer-events: auto; }
.fragment-quick { grid-area: quick; position: relative; z-index: 2; display: flex; align-items: center; }
.fragment-quick:empty { display: none; }
.fragment.is-foldable .fragment-body { grid-area: body; }
.fragment.is-foldable .fragment-actions { margin-top: 0.9rem; }
.fragment.is-foldable .fragment-actions > .fragment-journeys { flex: 1 1 100%; width: 100%; }
/* The chevron before the number: points right folded, down open. */
.is-foldable .fragment-num::before, .is-foldable .shard-num::before {
  content: ""; display: inline-block; width: 0.42em; height: 0.42em; margin-right: 0.7em; vertical-align: 0.12em;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(-45deg); transition: transform 0.25s;
}
.is-foldable.is-open .fragment-num::before, .is-foldable.is-open .shard-num::before { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) {
  .is-foldable .fragment-num::before, .is-foldable .shard-num::before { transition: none; }
}
@media (max-width: 34rem) {
  .fragment.is-foldable { grid-template-columns: 1fr auto; grid-template-areas: "num quick" "title quick" "state quick" "body body"; }
  .fragment.is-foldable .fragment-actions { margin-top: 0.9rem; }
}
.shard.is-foldable .fragment-toggle { z-index: 0; }
.shard.is-foldable .shard-num, .shard.is-foldable .shard-title { position: relative; z-index: 1; }
.shard.is-foldable .shard-num { pointer-events: none; }
.shard.is-foldable .shard-body, .shard.is-foldable .fragment-body { margin-top: 0; }
.shard.is-foldable .fragment-body-inner > .shard-line { padding-top: 0.35rem; }
