/*
  MusicMin — projector output styling.

  Shared by projector.html (the wall) and app.html's operator preview, so what the maestro sees in
  the preview pane is drawn by the same rules as what the room sees.

  The layer stack is the important part:

    .projection-backdrop   background — colour, gradient, pattern, later image or video
    .projection-scrim      a wash between background and words, so lyrics stay readable
    #projection-stage      the words; the only layer that redraws when the slide changes
    .projection-foreground logo, lower thirds, messages — reserved

  Keeping the background on its own layer is what lets a moving background keep playing while
  slides advance. Redrawing the whole stage on every slide, as the old output window did, would
  restart it every few seconds.
*/

:root {
  --projection-bg: #1e3a8a;
  --projection-lyrics: #ffffff;
  --projection-sections: #fde047;
  --projection-chords: #86efac;
  --projection-instructions: #fde68a;
  --projection-scrim: 0;
}

/* Page-level rules apply ONLY on projector.html, which sets class="projection-output" on its
   body. This stylesheet is also loaded by app.html so the operator's preview and the wall share
   one set of slide styles — and when these rules were unscoped they applied there too, so
   `overflow: hidden` stopped every page in the app from scrolling and `user-select: none` made
   its text unselectable. Slide styling is safe to share; page styling is not. */
body.projection-output * { box-sizing: border-box; }

html:has(body.projection-output), body.projection-output {
  width: 100%; height: 100%; margin: 0; overflow: hidden;
  background: var(--projection-bg);
  font-family: var(--mm-font-sans, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
  color: var(--projection-lyrics);
}

.projection-root { position: relative; width: 100%; height: 100%; overflow: hidden; }

.projection-backdrop {
  position: absolute; inset: 0; z-index: 0;
  background: var(--projection-bg);
  background-size: cover; background-position: center;
  opacity: 0;
  transition: none;
  will-change: opacity, transform;
}
.projection-backdrop.is-active { opacity: 1; }
.projection-backdrop video,
.projection-backdrop img { width: 100%; height: 100%; object-fit: cover; display: block; }

.projection-scrim {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: #000;
  opacity: calc(var(--projection-scrim) / 100);
  transition: opacity .3s ease;
}

#projection-stage {
  position: absolute; inset: 0; z-index: 2;
  width: 100%; height: 100%;
}
.projection-transition-cover {
  position: absolute; inset: 0; z-index: 8; pointer-events: none;
  background: #000; opacity: 0; will-change: opacity;
}

/* Two of these sit on top of each other so the words can cross-fade. An innerHTML swap cannot
   cross-fade with itself, and doing it here rather than on the whole stage is what leaves the
   backdrop completely untouched — which is what lets a moving background keep running while the
   lyrics change.

   The hidden one is opacity 0, never display none: the auto-fitter measures it before it is shown,
   and a display-none element reports no height at all. */
.projection-text-layer {
  position: absolute; inset: 0;
  /* A share of the box rather than a viewport measurement, so the margin round the words is the
     same fraction on the wall and in the operator's preview. With `5vw` the two were measuring
     different things — the preview is a panel inside a page, not the screen — and words anchored
     to a third landed at visibly different heights in the two places. */
  padding: calc(4.5% + var(--projection-safe-inset, 0px));
  display: flex; justify-content: center;
  align-items: center;
  opacity: 0; pointer-events: none;
  /* Promoted to its own compositor layer. Transitions are sequenced by projector.html so two
     complete lyric blocks are never readable at the same time. */
  will-change: opacity;
  backface-visibility: hidden;
  transition: none;
}
.projection-text-layer.is-active {
  opacity: 1;
}

/* Where the words sit vertically. The fitter is told the matching height band separately — CSS
   alone would size the text to fill the screen and only then push it into a third.

   These are not scoped to .projection-root, because the operator's preview in the control view has
   to be styled by exactly the same rules. A preview that showed centred words while the wall showed
   them in the bottom third is worse than no preview: it is a preview that lies. On the wall the
   class sits on the root and positions the text layer; in the preview it sits on the preview box,
   which is itself the flex container. */
.pos-top    .projection-text-layer, .projection-preview.pos-top    { align-items: flex-start; }
.pos-middle .projection-text-layer, .projection-preview.pos-middle { align-items: center; }
.pos-bottom .projection-text-layer, .projection-preview.pos-bottom { align-items: flex-end; }

/* The preview keeps the same proportional margin as the text layer it stands for, so a block put
   in the top third sits at the same height in both. */
.projection-preview { padding: 4.5%; }

/* The rows are named individually rather than left to inherit: .projection-lyric-row sets its own
   text-align: center, so an alignment set only on the container never reached the words. */
.align-left  .projection-slide-inner,
.align-left  .projection-lines { align-items: flex-start; }
.align-left  .projection-lines,
.align-left  .projection-lyric-row,
.align-left  .projection-chord-only,
.align-left  .projection-section-label,
.align-left  .projection-song-title,
.align-left  .projection-song-artist { text-align: left; }

.align-right .projection-slide-inner,
.align-right .projection-lines { align-items: flex-end; }
.align-right .projection-lines,
.align-right .projection-lyric-row,
.align-right .projection-chord-only,
.align-right .projection-section-label,
.align-right .projection-song-title,
.align-right .projection-song-artist { text-align: right; }

#projection-stage.blackout { background: #000; z-index: 5; }
#projection-stage.blackout > * { display: none; }
/* Clearing the words is not the same as blacking out: the background keeps running, which is
   what an operator wants between songs. */
#projection-stage.cleared > * { display: none; }

.projection-foreground { position: absolute; inset: 0; z-index: 3; pointer-events: none; }

.projection-slide-inner {
  width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .42em; text-align: center; line-height: 1.12;
  font-size: var(--projection-font-size, clamp(38px, 5.1vw, 86px));
}
.projection-title-slide .projection-song-title { color: var(--projection-sections); font-size: 1.35em; font-weight: 850; line-height: 1.05; text-wrap: balance; }
.projection-title-slide .projection-song-artist { color: var(--projection-lyrics); font-size: .68em; font-weight: 600; opacity: .86; }
.projection-timer-clock { color:var(--projection-lyrics); font-size:2.2em; font-weight:850; line-height:1; letter-spacing:.02em; font-variant-numeric:tabular-nums; }
.projection-timer-clock.is-done { color:var(--projection-sections); font-size:1.35em; }
.projection-timer-slide.size-small .projection-timer-clock { font-size:1.25em; }
.projection-timer-slide.size-medium .projection-timer-clock { font-size:1.7em; }
.projection-timer-slide.size-large .projection-timer-clock { font-size:2.2em; }
.projection-timer-slide.size-xlarge .projection-timer-clock { font-size:2.8em; }
.projection-timer-slide { height:100%; }
.projection-timer-slide.position-top-left { align-items:flex-start; justify-content:flex-start; text-align:left; }
.projection-timer-slide.position-top-center { align-items:center; justify-content:flex-start; }
.projection-timer-slide.position-top-right { align-items:flex-end; justify-content:flex-start; text-align:right; }
.projection-timer-slide.position-middle-left { align-items:flex-start; justify-content:center; text-align:left; }
.projection-timer-slide.position-center { align-items:center; justify-content:center; }
.projection-timer-slide.position-middle-right { align-items:flex-end; justify-content:center; text-align:right; }
.projection-timer-slide.position-bottom-left { align-items:flex-start; justify-content:flex-end; text-align:left; }
.projection-timer-slide.position-bottom-center { align-items:center; justify-content:flex-end; }
.projection-timer-slide.position-bottom-right { align-items:flex-end; justify-content:flex-end; text-align:right; }
.projection-timer-overlay { position:absolute; display:flex; align-items:center; gap:.65em; padding:.5em .8em; border:1px solid rgba(255,255,255,.24); border-radius:.35em; background:rgba(3,7,18,.82); color:#fff; font-size:clamp(22px,2.7vw,50px); font-weight:800; font-variant-numeric:tabular-nums; box-shadow:0 .25em .8em rgba(0,0,0,.28); }
.projection-timer-overlay.size-small { font-size:clamp(16px,1.7vw,32px); }
.projection-timer-overlay.size-medium { font-size:clamp(22px,2.7vw,50px); }
.projection-timer-overlay.size-large { font-size:clamp(30px,3.8vw,70px); }
.projection-timer-overlay.size-xlarge { font-size:clamp(40px,5.2vw,96px); }
.projection-timer-overlay.position-top-left { left:3%; top:5.33%; }
.projection-timer-overlay.position-top-center { left:50%; top:5.33%; transform:translateX(-50%); }
.projection-timer-overlay.position-top-right { right:3%; top:5.33%; }
.projection-timer-overlay.position-middle-left { left:3%; top:50%; transform:translateY(-50%); }
.projection-timer-overlay.position-center { left:50%; top:50%; transform:translate(-50%,-50%); }
.projection-timer-overlay.position-middle-right { right:3%; top:50%; transform:translateY(-50%); }
.projection-timer-overlay.position-bottom-left { left:3%; bottom:5.33%; }
.projection-timer-overlay.position-bottom-center { left:50%; bottom:5.33%; transform:translateX(-50%); }
.projection-timer-overlay.position-bottom-right { right:3%; bottom:5.33%; }
.projection-timer-label { font-size:.56em; opacity:.82; }
.projection-overlay-clock.is-done { color:var(--projection-sections); }
.projection-section-label { display: inline-flex; align-items: center; max-width: 92%; padding: .22em .58em; border: 1px solid var(--projection-sections); border-radius: 999px; color: var(--projection-sections); font-size: .52em; font-weight: 850; letter-spacing: .04em; text-transform: uppercase; }
.projection-lyric-row mark { padding:.02em .14em; border-radius:.12em; background:#fde047; color:#111827; }
.projection-lyric-row u { text-underline-offset:.12em; text-decoration-thickness:.07em; }
.projection-list-row {
  width:min(88%,1200px); display:grid; grid-template-columns:2.15em minmax(0,1fr);
  align-items:start; margin-inline:auto; text-align:left !important; text-wrap:pretty;
}
.projection-list-row > span:last-child { min-width:0; text-align:left; }
.projection-list-marker {
  color:var(--projection-sections); font-weight:800; font-variant-numeric:tabular-nums;
  text-align:right; padding-right:.55em;
}
.align-left .projection-list-row { margin-left:0; margin-right:auto; }
.align-right .projection-list-row { margin-left:auto; margin-right:0; }
.projection-section-instruction { margin-left: .6em; color: var(--projection-instructions); font-size: .86em; font-style: italic; font-weight: 650; text-transform: none; }
.projection-lines { width: 100%; display: flex; flex-direction: column; align-items: center; gap: .34em; }
.projection-lyric-row { max-width: 100%; color: var(--projection-lyrics); font-weight: 760; text-align: center; text-wrap: balance; }
.projection-segments { display: inline-flex; max-width: 100%; align-items: flex-end; justify-content: center; flex-wrap: wrap; column-gap: 0; row-gap: .24em; }
.projection-segment { display: inline-grid; grid-template-rows: auto auto; align-items: end; min-width: max-content; white-space: pre; text-align: left; vertical-align: bottom; }
.projection-chord { min-height: .92em; color: var(--projection-chords); font-size: .63em; font-weight: 850; line-height: 1; text-align: left; white-space: nowrap; }
.projection-lyric { color: var(--projection-lyrics); line-height: 1.08; white-space: pre; }
.projection-chord-only { color: var(--projection-chords); font-size: .78em; font-weight: 850; letter-spacing: .025em; }
.projection-instruction { padding: 0 .25em .06em; border-radius: .18em; color: var(--projection-instructions); background: rgba(245, 158, 11, .16); font-size: .7em; font-style: italic; font-weight: 650; }
.projection-notes-slide { width: min(86%, 1200px); align-items: flex-start; text-align: left; padding: 1em 1.1em; border: 2px solid var(--projection-instructions); border-radius: .55em; background: rgba(15, 23, 42, .42); }
.projection-notes-label { margin-bottom: .55em; color: var(--projection-instructions); font-size: .52em; font-weight: 850; letter-spacing: .08em; text-transform: uppercase; }
.projection-notes-text { width: 100%; color: var(--projection-lyrics); font-size: .82em; font-weight: 720; line-height: 1.35; white-space: pre-wrap; }

/* Text treatment. A shadow alone disappears on a light background, so outline and glow are
   available too — this is what keeps words legible once a photo sits behind them. */
.projection-text-shadow .projection-lyric-row,
.projection-text-shadow .projection-song-title,
.projection-text-shadow .projection-song-artist { text-shadow: 0 .04em .12em rgba(0, 0, 0, .75); }
.projection-text-outline .projection-lyric-row,
.projection-text-outline .projection-song-title { -webkit-text-stroke: .012em rgba(var(--projection-glow-rgb, 0, 0, 0), .85); paint-order: stroke fill; }
/* The glow colour is a choice. Black is the default and what it always was, but a dark background
   swallows a black glow entirely — there the halo has to be lighter than the words are sitting on,
   which is the whole reason to reach for a glow rather than a shadow. */
.projection-text-glow .projection-lyric-row,
.projection-text-glow .projection-song-title {
  text-shadow: 0 0 .35em rgba(var(--projection-glow-rgb, 0, 0, 0), .95),
               0 0 .12em rgba(var(--projection-glow-rgb, 0, 0, 0), .9);
}

/* ------------------------------------------------------------ background presets
   All CSS. Nothing to download, nothing to upload, and they work offline from the first run. */

.projection-bg-midnight       { background: linear-gradient(160deg, #0b1220 0%, #131f3a 45%, #0b1220 100%); }
.projection-bg-indigo-violet  { background: linear-gradient(155deg, #1e1b4b 0%, #4c1d95 55%, #1e1b4b 100%); }
.projection-bg-deep-teal      { background: linear-gradient(165deg, #042f2e 0%, #0f4c4a 50%, #042f2e 100%); }
.projection-bg-amber-dusk     { background: linear-gradient(160deg, #2b1503 0%, #7c2d12 55%, #2b1503 100%); }
.projection-bg-soft-cream     { background: linear-gradient(165deg, #fdf6e3 0%, #f5e7c8 100%); }

.projection-bg-charcoal-glow {
  background:
    radial-gradient(ellipse 70% 55% at 50% 42%, rgba(99, 102, 241, .22) 0%, rgba(17, 24, 39, 0) 70%),
    radial-gradient(ellipse 120% 90% at 50% 120%, rgba(0, 0, 0, .55) 0%, rgba(0, 0, 0, 0) 60%),
    #111827;
}

.projection-bg-starfield {
  background:
    radial-gradient(1.5px 1.5px at 18% 22%, rgba(255, 255, 255, .85) 50%, transparent 52%),
    radial-gradient(1.5px 1.5px at 72% 14%, rgba(255, 255, 255, .7) 50%, transparent 52%),
    radial-gradient(1.5px 1.5px at 41% 68%, rgba(255, 255, 255, .75) 50%, transparent 52%),
    radial-gradient(1.5px 1.5px at 87% 61%, rgba(255, 255, 255, .6) 50%, transparent 52%),
    radial-gradient(1.5px 1.5px at 29% 88%, rgba(255, 255, 255, .65) 50%, transparent 52%),
    radial-gradient(2px 2px at 62% 40%, rgba(255, 255, 255, .5) 50%, transparent 52%),
    radial-gradient(ellipse 80% 60% at 50% 45%, rgba(30, 58, 138, .35) 0%, rgba(7, 11, 24, 0) 72%),
    #070b18;
}

/* ------------------------------------------------------- on-screen controls
   For driving the output directly — a tablet at the desk, or a mouse on the laptop that is
   running it. Hidden until the operator moves or touches, so nothing sits over the words during
   a service, and out of the way again a few seconds later. */

.projection-touch { position: absolute; inset: 0; z-index: 4; display: none; }
.projection-root.touch-enabled .projection-touch { display: block; }
/* The tap zones stop short of the bottom strip. They used to run the full height, so the control
   bar sat inside them and every press on a button was taken as a tap-to-advance instead. */
.projection-tap-zone { position: absolute; top: 0; bottom: 104px; width: 33%; -webkit-tap-highlight-color: transparent; }
.projection-tap-prev { left: 0; }
.projection-tap-next { right: 0; }

.projection-controls {
  position: absolute; left: 50%; bottom: max(18px, env(safe-area-inset-bottom, 18px));
  /* Above the tap layer. Without this the bar painted underneath it: visible, and unclickable,
     because the tap zone was in front catching everything. */
  z-index: 7;
  transform: translateX(-50%) translateY(12px);
  display: flex; gap: 8px; padding: 8px;
  border-radius: 999px; background: rgba(10, 12, 20, .72);
  backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, .14);
  opacity: 0; pointer-events: none; transition: opacity .22s ease, transform .22s ease;
}
.projection-root.controls-visible .projection-controls { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
.projection-controls button {
  min-width: 46px; min-height: 46px; padding: 0 14px;
  border: 0; border-radius: 999px; background: rgba(255, 255, 255, .12);
  color: #fff; font: inherit; font-size: 15px; font-weight: 700; cursor: pointer;
}
.projection-controls button:active { background: rgba(255, 255, 255, .26); }

.projection-fullscreen-gate {
  position:absolute; inset:0; z-index:10; display:grid; place-items:center;
  padding:24px; background:rgba(3,7,18,.72); backdrop-filter:blur(8px);
}
.projection-fullscreen-gate[hidden] { display:none; }
.projection-fullscreen-card {
  width:min(560px,90vw); padding:28px; border:1px solid rgba(255,255,255,.2);
  border-radius:20px; background:rgba(10,12,28,.96); color:#fff; text-align:center;
  box-shadow:0 24px 70px rgba(0,0,0,.5);
}
.projection-fullscreen-card strong,.projection-fullscreen-card span { display:block; }
.projection-fullscreen-card strong { font-size:clamp(22px,3vw,34px); line-height:1.15; }
.projection-fullscreen-card span { margin-top:10px; color:rgba(255,255,255,.76); font-size:16px; }
.projection-fullscreen-card button {
  margin-top:22px; min-height:54px; padding:0 24px; border:0; border-radius:999px;
  background:#2563eb; color:#fff; font:inherit; font-size:18px; font-weight:800; cursor:pointer;
}
.projection-fullscreen-card button:hover { background:#1d4ed8; }

.projection-status {
  position: absolute; top: calc(16px + max(env(safe-area-inset-top, 0px), var(--safe-area-inset-top, 0px))); left: 50%; transform: translateX(-50%);
  z-index: 8; padding: 8px 16px; border-radius: 999px;
  background: rgba(10, 12, 20, .8); color: #fff; font-size: 14px; font-weight: 650;
  opacity: 0; transition: opacity .25s ease; pointer-events: none;
}
.projection-root.status-visible .projection-status { opacity: 1; }

body.projection-output { -webkit-user-select: none; user-select: none; touch-action: manipulation; }

@media (prefers-reduced-motion: reduce) {
  .projection-backdrop, .projection-scrim, .projection-controls,
  .projection-text-layer { transition: none; }
  .projection-text-layer.enter-slide,
  .projection-text-layer.enter-zoom { transform: none; }
}

/* A media slide fills the stage. The stage keeps its padding for words, so media opts out of it
   and covers the frame the way a background would. */
.projection-media { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.projection-text-layer:has(.projection-media) { padding: 0; }

/* The operator's preview boxes are not the projector stage, so media needs to be told to fit
   inside them too — otherwise a picture drew at its natural size and a tall one ran straight out
   of the box. */
#slide-preview, .preview-slide, #presenter-next-slide-preview { position: relative; overflow: hidden; }
#slide-preview .projection-media,
.preview-slide .projection-media,
#presenter-next-slide-preview .projection-media {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
}

/* ------------------------------------------------------- video links (YouTube / Drive)

   A borrowed player, filling the stage the way a media slide does. The still sits underneath the
   frame rather than beside it, so the room has a picture to look at from the first frame of the
   transition until the player has actually produced one — the frame arrives black and spinning,
   and a black rectangle sliding onto a screen is what makes a video slide look broken. */
/* container-type so the play badge can be sized against the box it sits in — the same markup has
   to read on a 4-metre screen and in the 200px next-slide card. */
.projection-embed { position: absolute; inset: 0; overflow: hidden; background: #000; container-type: size; }
.projection-text-layer:has(.projection-embed) { padding: 0; }
.projection-embed-poster {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  opacity: 1; transition: opacity .4s ease;
}
.projection-embed.is-playing .projection-embed-poster { opacity: 0; }
.projection-embed-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
/* Above the frame, catching every pointer event. This is what keeps YouTube's own title bar and
   Watch on YouTube button off the wall — they appear on hover, and hover never gets there — and
   it is the only surface a "tap to start the sound" instruction can actually be tapped on. */
.projection-embed-shield { position: absolute; inset: 0; background: transparent; }
/* The placeholder the operator's preview, the next-slide card and the phone all show instead of
   a second player. */
.projection-embed.is-placeholder { background: #000; }
.projection-embed-badge {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: grid; place-items: center; width: 14%; aspect-ratio: 1; min-width: 28px;
  border-radius: 50%; background: rgba(15, 23, 42, .62); color: #fff;
  font-size: clamp(12px, 5cqw, 42px); line-height: 1; padding-left: .08em;
  box-shadow: 0 4px 18px rgba(0, 0, 0, .45);
}
#slide-preview .projection-embed,
.preview-slide .projection-embed,
#presenter-next-slide-preview .projection-embed { position: absolute; inset: 0; }


/* ------------------------------------------------------------------- plate
   A box behind the words, for when they sit over a photograph or a moving background. `lines`
   gives each line its own box and hugs the text; `band` puts one box behind the whole block. */
/* Defaults live on :root so the preview boxes inherit them as well as the projector stage. */
:root {
  --projection-plate-rgb: 0, 0, 0;
  --projection-plate-alpha: 0;
  --projection-plate-pad-x: .5em;
  --projection-plate-pad-y: .22em;
  --projection-plate-radius: .18em;
}
.plate-lines .projection-lyric-row,
.plate-lines .projection-chord-only,
.plate-lines .projection-song-title,
.plate-band .projection-slide-inner {
  padding: var(--projection-plate-pad-y) var(--projection-plate-pad-x);
  border-radius: var(--projection-plate-radius);
  background: rgba(var(--projection-plate-rgb), var(--projection-plate-alpha));
}
/* The band takes in the section label as well as the lines. Banding only the lyrics left the
   section name floating above the panel, reading as though it belonged to something else. */
.plate-band .projection-slide-inner {
  width: auto; max-width: 92%;
  padding: calc(var(--projection-plate-pad-y) * 1.6) calc(var(--projection-plate-pad-x) * 1.2);
}

/* ------------------------------------------------------------------ standby
   What the screen holds when nothing is being projected. Default is a black screen, which is what
   the projector always did — these styles only come into play once someone chooses otherwise. */
.projection-standby { gap: .5em; }
.projection-standby-message {
  max-width: 86%; color: var(--projection-lyrics); font-size: .62em; font-weight: 680;
  line-height: 1.25; text-wrap: balance;
}
.projection-standby-message.is-large { font-size: .95em; font-weight: 780; }
.projection-standby-clock {
  color: var(--projection-sections); font-size: 1.9em; font-weight: 860;
  font-variant-numeric: tabular-nums; line-height: 1; letter-spacing: .01em;
}
/* Tabular figures matter here: without them the clock jitters sideways every second. */
.projection-standby-clock.is-done { color: var(--projection-instructions); font-size: 1.15em; }

/* --------------------------------------------------------------- foreground
   The caption, the alert and the logo. They sit over everything without touching the slide, so a
   lower third can name a speaker while the words carry on behind it. */
.projection-foreground.has-content { pointer-events: none; }

.projection-lower-third {
  position: absolute; left: 6%; right: 6%; bottom: 7%;
  display: flex; flex-direction: column; gap: .12em;
  padding: .7em 1em; border-left: 5px solid var(--projection-sections); border-radius: 6px;
  background: rgba(8, 12, 22, .74);
  font-size: clamp(16px, 2.1vw, 40px);
  animation: projection-lower-in .32s ease-out;
}
/* Stop short of a logo sharing the same strip, rather than running underneath it. */
.projection-lower-third.clear-right { right: calc(6% + var(--projection-logo-room, 16%)); }
.projection-lower-third.clear-left  { left:  calc(6% + var(--projection-logo-room, 16%)); }
.projection-lower-title { color: var(--projection-lyrics); font-weight: 820; line-height: 1.1; }
.projection-lower-subtitle { color: var(--projection-sections); font-size: .66em; font-weight: 650; }
@keyframes projection-lower-in { from { opacity: 0; transform: translateY(14%); } to { opacity: 1; transform: none; } }

.projection-alert {
  position: absolute; left: 0; right: 0; top: 0;
  padding: calc(.7em + max(env(safe-area-inset-top, 0px), var(--safe-area-inset-top, 0px))) 1.2em .7em; background: rgba(185, 28, 28, .92);
  font-size: clamp(15px, 1.9vw, 34px); text-align: center;
  animation: projection-alert-in .28s ease-out;
}
.projection-alert-text { color: #fff; font-weight: 820; line-height: 1.2; }
@keyframes projection-alert-in { from { opacity: 0; transform: translateY(-100%); } to { opacity: 1; transform: none; } }

.projection-logo { position: absolute; height: auto; opacity: .92; }
.projection-logo.logo-top-left { top: 3%; left: 3%; }
.projection-logo.logo-top-right { top: 3%; right: 3%; }
.projection-logo.logo-bottom-left { bottom: 3%; left: 3%; }
.projection-logo.logo-bottom-right { bottom: 3%; right: 3%; }

@media (prefers-reduced-motion: reduce) {
  .projection-lower-third, .projection-alert { animation: none; }
}

/* The verse number, set small and raised the way a printed Bible sets it. `line-height: 0` keeps
   it from adding leading to the line it sits on, so verses stay evenly spaced. */
.projection-verse-number {
  margin-right: .16em;
  color: var(--projection-sections);
  font-size: .46em;
  font-weight: 850;
  line-height: 0;
  vertical-align: super;
  opacity: .95;
}
