/* Scale Info · Jarvis (built and driven by assets/jarvis.js; the big orb is drawn by assets/plexus.js): the daily
 * briefing's side orb, its once-a-day card, and the frame the briefing opens in — in the middle of the screen, with
 * everything else blurred behind it. Made of the dashboard's own parts (.card brackets, .btn, .seg, .badge, the Label
 * type) and coloured only from the tokens in app.css's :root: the orb glows in the logo's lime (--accent).
 * Fixed or in the top layer, so nothing on the page ever moves for it, and closed it covers nothing but its own 44px
 * corner. Motion is transform and opacity only; the side orb moves only while the voice does (or thinks), and reduced
 * motion drops every pulse and every entrance. */

/* A zero-size anchor in the corner that only holds the geometry: the orb and the card are each fixed on their own, so
   one appearing or going never moves the other (no layout shift, not even inside). */
.jv {
  --jv-r: max(var(--s-6), env(safe-area-inset-right, 0px));
  --jv-b: calc(var(--tabbar-h, 0px) + var(--safe-bottom, 0px) + var(--s-6));
  --jv-orb: 44px;
  --jv-glow: rgba(161, 249, 99, 0.45);      /* = --accent-glow */
  --jv-glow-soft: rgba(161, 249, 99, 0.16);
  --jv-hot: #E4FDD2;                         /* a near-white lime: --accent-hover lifted toward white */
  --jv-icon-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");
  position: fixed; z-index: 35; right: 0; bottom: 0; width: 0; height: 0;
}
/* Out of the way while one of the page's dialogs is open (jarvis.js sets it from the dialogs' `open`). */
.jv[data-covered] { display: none; }

/* ---------- the side orb: a small glowing lime sphere with the plexus mark ---------- */
.jv-orb {
  position: fixed; right: var(--jv-r); bottom: var(--jv-b);
  display: grid; place-items: center;
  width: var(--jv-orb); height: var(--jv-orb); margin: 0; padding: 0;
  border: 1px solid rgba(161, 249, 99, 0.55); border-radius: 50%;
  background:
    radial-gradient(circle at 50% 42%, rgba(161, 249, 99, 0.30), rgba(161, 249, 99, 0.07) 58%, transparent 74%),
    var(--surface);
  color: var(--accent);
  /* The glow: painted with the orb, never animated (the pulse below is a separate ring's transform and opacity). */
  box-shadow: 0 0 0 1px rgba(161, 249, 99, 0.08), 0 0 14px var(--jv-glow), 0 0 32px var(--jv-glow-soft);
  cursor: pointer;
  transition: transform var(--dur-1) var(--ease);
  animation: jv-in var(--dur-3) var(--ease) backwards;
}
.jv-orb:active { transform: scale(0.94); }
@media (hover: hover) {
  .jv-orb:hover { border-color: var(--accent); background-color: var(--surface-hover); }
}
.jv-orb > * { grid-area: 1 / 1; }
.jv-mark { width: 30px; height: 30px; overflow: visible; pointer-events: none; }
.jv-m-rim { fill: none; stroke: currentColor; stroke-width: 0.6; opacity: 0.45; }
.jv-m-net { fill: none; stroke: currentColor; stroke-width: 0.7; stroke-linecap: round; opacity: 0.8; }
.jv-m-dot { fill: var(--jv-hot); }
/* Two ripples on the orb's own edge, invisible until it speaks. */
.jv-wave { position: absolute; inset: -1px; border: 1px solid var(--accent); border-radius: 50%; opacity: 0; pointer-events: none; }

/* Speaking with the frame minimised: the rings ripple out and the mark breathes. Thinking: the mark turns. Paused:
   the glow dims and nothing moves. */
.jv:is([data-state="speaking"], [data-state="thinking"]) .jv-orb { border-color: var(--accent); }
.jv[data-state="speaking"] .jv-wave { animation: jv-ripple 2.4s cubic-bezier(0.22, 1, 0.36, 1) infinite; }
.jv[data-state="speaking"] .jv-wave + .jv-wave { animation-delay: -1.2s; }
.jv[data-state="speaking"] .jv-mark { animation: jv-breathe 0.9s ease-in-out infinite alternate; }
.jv[data-state="thinking"] .jv-mark { animation: jv-spin 2.4s linear infinite; }
.jv[data-state="paused"] .jv-orb { box-shadow: 0 0 0 1px rgba(161, 249, 99, 0.08), 0 0 8px var(--jv-glow-soft); }
.jv[data-state="paused"] .jv-mark { opacity: 0.6; }
/* Behind the open frame the orb is under the blur: it stays still there, so the blur is worked out once, not every
   frame. */
.jv[data-open] .jv-orb, .jv[data-open] .jv-wave, .jv[data-open] .jv-mark { animation: none; }
/* Today's briefing is waiting: the daily card stepped aside as soon as the page was used, and jarvis.js marks the orb
   (data-ready) until it is played or put off with Later. The ring is lit and the dashboard's "just changed" dot sits on
   the orb's shoulder, ringed in the page colour so it reads against the glow. Still: nothing pulses at rest. */
.jv[data-ready] .jv-orb { border-color: var(--accent); }
.jv[data-ready] .jv-orb::after {
  content: ""; position: absolute; top: 0; right: 0; width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 2px var(--bg), 0 0 8px var(--jv-glow); pointer-events: none;
}

@keyframes jv-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes jv-ripple { 0% { opacity: 0.7; transform: scale(1); } 100% { opacity: 0; transform: scale(1.85); } }
@keyframes jv-breathe { from { transform: scale(0.9); } to { transform: scale(1.1); } }
@keyframes jv-spin { to { transform: rotate(1turn); } }
@keyframes jv-fade { from { opacity: 0; } }
@keyframes jv-fade-out { to { opacity: 0; } }
@keyframes jv-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }

/* ---------- the daily card: a dashboard panel, lifted ---------- */
.jv .jv-card {
  --card-pad: var(--s-4);
  --bracket-l: 8px;
  position: fixed;
  border-color: var(--border-strong);
  box-shadow: var(--shadow-pop);
  animation: jv-in var(--dur-3) var(--ease) backwards;
}
/* One 36px row ("Good evening, Liam." · Play briefing · Later) in the main column's 40px top margin, its right edge on
   the content's. That band is empty at every desktop size, and the card shows only once the page has settled and
   steps aside at the first scroll, press or focus in the page (jarvis.js), so it never sits on a figure. Its overline
   and "Your daily briefing is ready." stay in it for a screen reader, which also hears the sentence from the live
   line, but are not shown: Play briefing says the same. Play opens the briefing's frame. */
.jv .jv-card {
  top: 2px; bottom: auto;
  right: max(var(--gutter), calc((100vw - var(--sidebar-w) - var(--content-max)) / 2));
  display: flex; flex-wrap: nowrap; align-items: center; gap: var(--s-3);
  width: auto; max-width: calc(100vw - var(--sidebar-w) - 2 * var(--gutter));
  min-height: 36px; padding: 3px 3px 3px var(--s-4);
}
.jv-card > .jv-head, .jv-card > .jv-sub {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.jv .jv-card .jv-hello { flex: 1 1 auto; min-width: 0; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jv .jv-card .jv-actions { flex: none; flex-wrap: nowrap; gap: var(--s-2); margin: 0; }
/* 28px buttons, like the lockbox's, so the row fits the band; on touch their hit areas reach 44px. */
.jv .jv-card .jv-actions > .btn { flex: none; height: 28px; padding: 0 var(--s-3); }
@media (pointer: coarse) {
  .jv .jv-card .jv-actions > .btn::after { inset: -8px -2px; }
}
.jv-head { display: flex; align-items: center; gap: var(--s-2); min-width: 0; margin-bottom: var(--s-3); }
.jv-head .panel-title { margin: 0; white-space: nowrap; }
.jv-head .panel-meta { margin: 0 0 0 auto; white-space: nowrap; }
.jv-hello { font-size: 17px; font-weight: 600; line-height: 1.3; letter-spacing: -0.01em; color: var(--text); }
.jv-sub { margin-top: var(--s-1); font-size: 14px; line-height: 1.5; color: var(--text-2); }
.jv-actions { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4); }
.jv-actions .btn-primary { flex: 1 1 auto; }

/* ---------- phones: the orb above the tab bar; the card in the top bar ---------- */
@media (max-width: 860px) {
  .jv {
    --jv-r: max(var(--s-4), env(safe-area-inset-right, 0px));
    --jv-b: calc(var(--tabbar-h, 56px) + var(--safe-bottom, 0px) + var(--s-3));
    /* Over the top bar, for the daily card below; the menus, toasts, the loading bar and the dialogs stay above. */
    z-index: 51;
  }
  /* The daily card takes the brand's place in the top bar, a 44px row from the left gutter to Sign out, so on a
     phone too it covers no part of the page. Sign out stays in view; the status line goes under the card with the
     brand until the first touch (the sample banner says the same thing meanwhile). */
  .jv .jv-card {
    top: calc(var(--safe-top, 0px) + (var(--topbar-h, 56px) - 44px) / 2);
    right: calc(max(var(--gutter), env(safe-area-inset-right, 0px)) + 44px + var(--s-2));
    left: max(var(--gutter), env(safe-area-inset-left, 0px));
    max-width: none; min-height: 44px; padding-left: var(--s-3);
  }
  .jv .jv-card .jv-actions > .btn { height: 36px; }
  .jv .jv-card .jv-actions > .btn::after { inset: -5px -4px; }
}
/* Narrow phones: the greeting keeps the room and the two buttons are icons (play, and a cross for Later); their
   words stay as their names, visually hidden (the lockbox's way). */
@media (max-width: 539px) {
  .jv .jv-card { gap: var(--s-2); }
  .jv .jv-card .jv-actions > .btn { width: 36px; padding: 0; gap: 0; font-size: 0; letter-spacing: 0; }
  .jv .jv-card .jv-play > span {
    position: absolute; left: 0; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  .jv .jv-card .jv-later::before {
    content: ""; flex: none; width: 14px; height: 14px; background: currentColor;
    -webkit-mask: var(--jv-icon-close) center / contain no-repeat; mask: var(--jv-icon-close) center / contain no-repeat;
  }
}
/* The smallest phones: about 130px for the greeting, so it may take two 14px lines in the row's 36px rather than end
   "Good evening, Li…". */
@media (max-width: 359px) {
  .jv .jv-card .jv-hello {
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
    font-size: 14px; line-height: 1.25; white-space: normal;
  }
}

/* ==========================================================================
   The frame: a modal <dialog> the size of the screen (its own padding is the gutter), the briefing's panel centred in
   it, and the page behind blurred and dimmed by its ::backdrop. A click on the dialog outside the panel is a click on
   the blur (jarvis.js closes on it). Closed, it is display: none and covers nothing.
   ========================================================================== */
.jv-frame:not([open]) { display: none; }
.jv-frame {
  --jv-orb-size: clamp(200px, calc(100vh - 220px), 320px);
  --jv-glow: rgba(161, 249, 99, 0.45);
  --jv-hot: #E4FDD2;
  position: fixed; inset: 0;
  width: 100%; height: 100%; max-width: none; max-height: none;
  margin: 0;
  padding: max(var(--s-4), env(safe-area-inset-top, 0px)) max(var(--s-4), env(safe-area-inset-right, 0px))
           max(var(--s-4), env(safe-area-inset-bottom, 0px)) max(var(--s-4), env(safe-area-inset-left, 0px));
  border: 0; background: transparent; color: var(--text);
  overflow: hidden; overscroll-behavior: contain;
}
@supports (height: 100dvh) {
  .jv-frame { --jv-orb-size: clamp(200px, calc(100dvh - 220px), 320px); }
}
.jv-frame[open] { display: grid; grid-template: minmax(0, 1fr) / minmax(0, 1fr); place-items: center; }
/* Everything behind it: blurred and dimmed, so only Jarvis and the briefing are in focus. Where blur is missing (or
   reduced transparency is asked for) a deeper flat dim instead. The values are literal: in older browsers ::backdrop
   does not inherit the page's custom properties. */
.jv-frame::backdrop { background: rgba(3, 5, 4, 0.84); }
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .jv-frame::backdrop {
    background: rgba(3, 5, 4, 0.52);
    -webkit-backdrop-filter: blur(14px) saturate(115%);
    backdrop-filter: blur(14px) saturate(115%);
  }
}
@media (prefers-reduced-transparency: reduce) {
  .jv-frame::backdrop { background: rgba(3, 5, 4, 0.9); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.jv-frame[open]::backdrop { animation: jv-fade 260ms cubic-bezier(0.16, 1, 0.3, 1); }
.jv-frame[data-closing]::backdrop { animation: jv-fade-out 200ms cubic-bezier(0.4, 0, 1, 1) forwards; }
/* The page's one moving backdrop layer (atmosphere.js's scan band, mouse screens only) stands down while the frame is
   open: anything moving under a backdrop blur makes the browser blur the whole screen again every frame. */
html[data-jv-open] .scan { display: none; }

/* The panel: the dashboard's card (opaque surface, hairline, corner brackets), lifted with a lime light. */
.jv-box {
  position: relative; isolation: isolate;
  display: flex; flex-direction: column;
  width: min(960px, 100%); max-height: 100%; min-height: 0;
  border: 1px solid var(--border-strong); border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: 0 0 0 1px rgba(161, 249, 99, 0.05), 0 28px 80px -24px rgba(0, 0, 0, 0.9), 0 0 90px -24px rgba(161, 249, 99, 0.22);
  transform-origin: 50% 50%;
}
.jv-box::before {
  content: ""; position: absolute; inset: -1px; z-index: 4; pointer-events: none;
  --bl: 12px;
  background:
    linear-gradient(var(--bracket) 0 0) top left / var(--bl) 1px no-repeat,
    linear-gradient(var(--bracket) 0 0) top left / 1px var(--bl) no-repeat,
    linear-gradient(var(--bracket) 0 0) top right / var(--bl) 1px no-repeat,
    linear-gradient(var(--bracket) 0 0) top right / 1px var(--bl) no-repeat,
    linear-gradient(var(--bracket) 0 0) bottom left / var(--bl) 1px no-repeat,
    linear-gradient(var(--bracket) 0 0) bottom left / 1px var(--bl) no-repeat,
    linear-gradient(var(--bracket) 0 0) bottom right / var(--bl) 1px no-repeat,
    linear-gradient(var(--bracket) 0 0) bottom right / 1px var(--bl) no-repeat;
}
/* A lime streak of light along the top edge. */
.jv-box::after {
  content: ""; position: absolute; top: -1px; left: 14%; right: 14%; height: 1px; z-index: 3; pointer-events: none;
  background: linear-gradient(90deg, transparent, var(--accent) 50%, transparent); opacity: 0.85;
}
/* The X: top right, a 44px target, outside the scrolling part so it never scrolls away. */
.jv-frame .jv-x {
  position: absolute; top: var(--s-2); right: var(--s-2); z-index: 5;
  width: 44px; height: 44px; border-color: transparent; color: var(--text-2);
}
.jv-frame .jv-x svg { width: 20px; height: 20px; }
@media (hover: hover) {
  .jv-frame .jv-x:hover { color: var(--text); }
}
.jv-frame .jv-x::after { content: none; }

/* What scrolls when the screen is too short for all of it (never the page behind). */
.jv-sheet {
  display: grid; grid-template-columns: var(--jv-orb-size) minmax(0, 1fr); align-items: center; gap: var(--s-7);
  min-height: 0; padding: var(--s-8) var(--s-8) var(--s-7) var(--s-7);
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
}

/* ---------- the orb ---------- */
.jv-stage {
  position: relative; display: grid; place-items: center;
  width: var(--jv-orb-size); aspect-ratio: 1; min-width: 0;
}
.jv-stage > * { grid-area: 1 / 1; }
/* A soft lime light behind the sphere. */
.jv-stage::before {
  content: ""; position: absolute; inset: -16%; z-index: -1; border-radius: 50%; pointer-events: none;
  background: radial-gradient(closest-side, rgba(161, 249, 99, 0.16), rgba(161, 249, 99, 0.05) 55%, transparent 72%);
}
/* The CSS orb: shown until plexus.js has drawn its first frame, and for good where it can't (no WebGL and no
   Canvas 2D, or the file failed to load): a glowing lime sphere with the plexus mark. */
.jv-sphere {
  display: grid; place-items: center; width: 76%; aspect-ratio: 1; border-radius: 50%; color: var(--accent);
  background: radial-gradient(circle at 50% 40%, rgba(228, 253, 210, 0.22), rgba(161, 249, 99, 0.12) 38%, rgba(161, 249, 99, 0.03) 66%, transparent 72%);
  box-shadow: inset 0 0 0 1px rgba(161, 249, 99, 0.18), 0 0 60px rgba(161, 249, 99, 0.12);
  transition: opacity 500ms ease;
}
.jv-sphere-mark { width: 92%; height: 92%; overflow: visible; }
.jv-sphere-mark .jv-m-net { stroke-width: 0.12; opacity: 0.7; }
.jv-sphere-mark .jv-m-rim { stroke-width: 0.1; opacity: 0.5; }
.jv-sphere-mark .jv-m-dot { r: 0.34px; }
.jv-stage.is-live .jv-sphere { opacity: 0; }
/* The plexus fills the square; its edge fades out in a circle. */
.jv-canvas {
  display: block; width: 100%; height: 100%; border-radius: 50%;
  -webkit-mask: radial-gradient(closest-side, #000 84%, transparent 100%); mask: radial-gradient(closest-side, #000 84%, transparent 100%);
  opacity: 0; transition: opacity 500ms ease;
}
.jv-stage.is-live .jv-canvas { opacity: 1; }
/* The sentence being said, soft over the lower middle of the sphere. */
.jv-caption {
  align-self: end; z-index: 2; max-width: 80%; margin-bottom: 21%; padding: 4px 10px;
  font-size: 14px; font-weight: 500; line-height: 1.45; text-align: center; color: var(--text);
  /* A soft dark backing so the words read cleanly over the sphere's lines. */
  background: rgba(6, 10, 6, 0.6); border-radius: 10px;
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.9);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
  overflow-wrap: anywhere; pointer-events: none;
}
.jv-caption:empty { display: none; }
/* "Thinking…" under the sphere while the figures are being fetched. */
.jv-thinking {
  display: none; align-self: end; z-index: 2; margin-bottom: 3%;
  font-family: var(--mono); font-size: 11px; font-weight: 500; line-height: 1.3; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--accent); text-shadow: 0 0 10px var(--jv-glow); pointer-events: none;
}
.jv-dots { display: inline-block; animation: jv-pulse 1.4s ease-in-out infinite; }
.jv-frame[data-state="thinking"] .jv-thinking { display: block; }
.jv-frame[data-state="thinking"] .jv-caption { display: none; }

/* ---------- the words beside it ---------- */
.jv-body { display: grid; gap: var(--s-4); min-width: 0; }
.jv-fhead { display: grid; gap: var(--s-2); min-width: 0; padding-right: var(--s-6); }
.jv-label { justify-self: start; gap: var(--s-2); height: 24px; padding: 0 var(--s-3); }
.jv-spark { font-family: var(--sans); font-size: 12px; letter-spacing: 0; text-shadow: 0 0 8px var(--jv-glow); }
.jv-title {
  margin: 0; font-family: var(--sans); font-size: var(--fs-title); font-weight: 600; line-height: 1.15;
  letter-spacing: -0.02em; color: var(--text); overflow-wrap: anywhere;
}
.jv-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-1) var(--s-2); min-height: 22px;
  font-family: var(--mono); font-size: 11px; font-weight: 500; line-height: 1.3; letter-spacing: 0.06em; text-transform: uppercase;
  font-variant-numeric: tabular-nums; color: var(--text-2);
}
.jv-sep { color: var(--text-3); }
/* The span switch (24 hrs / 7 days / 30 days) under the meta line: the dashboard's own segment, 44px on touch. */
.jv-frame .jv-periods { --seg-h: 34px; display: inline-flex; justify-self: start; margin-top: var(--s-1); }
.jv-frame .jv-period { flex: none; min-width: 72px; padding: 0 var(--s-3); text-transform: none; letter-spacing: 0.04em; }
@media (pointer: coarse) {
  .jv-frame .jv-periods { --seg-h: 44px; }
}
.jv-voice { border-color: var(--line-ui); background: transparent; color: var(--accent-hover); }
.jv-note { font-size: 14px; line-height: 1.5; color: var(--text-2); }

/* The transcript: one sentence per line. The line being said is bright on a faint lime band with a 2px lime bar,
   the lines said are grey, the lines to come dimmer; top and bottom fade out. Tap a line to go to it. */
.jv-transcript {
  position: relative; max-height: 248px; margin: 0; padding: var(--s-3) var(--s-1); list-style: none;
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: none;
  -webkit-mask: linear-gradient(transparent, #000 var(--s-4), #000 calc(100% - var(--s-4)), transparent);
  mask: linear-gradient(transparent, #000 var(--s-4), #000 calc(100% - var(--s-4)), transparent);
}
.jv-transcript::-webkit-scrollbar { display: none; }
.jv-transcript:empty { display: none; }
.jv-line {
  display: block; width: 100%; margin: 0; padding: var(--s-1) var(--s-3); border: 0; border-radius: var(--r-sm);
  background: none; text-align: left; font-family: var(--sans); font-size: 17px; font-weight: 400; line-height: 1.5;
  letter-spacing: 0; text-transform: none; color: var(--text-3); overflow-wrap: anywhere; cursor: pointer;
}
span.jv-line { cursor: auto; color: var(--text); }
.jv-transcript .is-past .jv-line { color: var(--text-2); }
.jv-transcript .is-now .jv-line {
  color: var(--text);
  background: linear-gradient(90deg, rgba(161, 249, 99, 0.11), rgba(161, 249, 99, 0.03) 70%, transparent);
  box-shadow: inset 2px 0 0 var(--accent);
}
.jv-transcript .jv-line:focus-visible { box-shadow: inset 0 0 0 2px var(--accent); }
@media (hover: hover) {
  .jv-transcript button.jv-line:hover { color: var(--text); }
}

/* The progress bar: thin, lime, lit. */
.jv-progress { height: 3px; border-radius: var(--r-full); background: var(--line-neutral); overflow: hidden; }
.jv-progress > span {
  display: block; height: 100%; border-radius: inherit; background: var(--accent);
  box-shadow: 0 0 10px var(--jv-glow); transform-origin: left; transform: scaleX(0);
}

/* The controls: play / pause (the one filled button), replay, and the speed switch. 44px targets at every size. */
.jv-controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.jv-controls .btn-icon { width: 44px; height: 44px; }
.jv-controls .btn-icon svg { width: 20px; height: 20px; }
.jv-controls .btn::after { content: none; }
.jv-toggle { box-shadow: 0 0 16px rgba(161, 249, 99, 0.25); }
.jv-frame .jv-rates { --seg-h: 42px; display: inline-flex; flex: none; }
.jv-frame .jv-rate { flex: none; min-width: 64px; padding: 0 var(--s-3); text-transform: none; letter-spacing: 0.04em; }

/* ---------- phones: one column, the orb on top, everything centred and fitted to the screen ---------- */
@media (max-width: 760px) {
  .jv-frame { --jv-orb-size: clamp(168px, min(64vw, calc(100vh - 600px)), 260px); }
  @supports (height: 100dvh) {
    .jv-frame { --jv-orb-size: clamp(168px, min(64vw, calc(100dvh - 600px)), 260px); }
  }
  .jv-box { width: min(520px, 100%); }
  /* A column whose transcript gives up its height first, so the head and the controls stay on screen. */
  .jv-sheet {
    display: flex; flex-direction: column; align-items: center; gap: var(--s-4);
    flex: 1 1 auto; padding: var(--s-6) var(--s-4) var(--s-5);
  }
  .jv-stage { flex: none; }
  .jv-body { display: flex; flex-direction: column; flex: 1 1 auto; align-self: stretch; gap: var(--s-3); min-height: 0; }
  .jv-fhead { justify-items: center; padding-right: 0; text-align: center; }
  .jv-label { justify-self: center; }
  .jv-meta { justify-content: center; }
  .jv-frame .jv-periods { justify-self: center; }
  .jv-note { text-align: center; }
  .jv-transcript { flex: 1 1 auto; min-height: 112px; max-height: none; }
  .jv-line { font-size: 16px; }
  .jv-progress { flex: none; }
  .jv-controls { flex: none; justify-content: center; }
}
@media (pointer: coarse) {
  .jv-transcript .jv-line { min-height: 44px; padding-top: 10px; padding-bottom: 10px; }
}

/* ---------- still ---------- */
@media (prefers-reduced-motion: reduce) {
  .jv .jv-orb, .jv .jv-card, .jv .jv-wave, .jv .jv-mark, .jv-dots, .jv-frame[open]::backdrop,
  .jv-frame[data-closing]::backdrop { animation: none !important; }
  .jv .jv-orb { transition: none; }
  .jv-sphere, .jv-canvas { transition: none; }
}
@media (forced-colors: active) {
  .jv-orb { border-color: ButtonText; forced-color-adjust: none; background: Canvas; color: ButtonText; box-shadow: none; }
  .jv-m-dot, .jv[data-ready] .jv-orb::after { forced-color-adjust: none; fill: ButtonText; background: CanvasText; box-shadow: none; }
  .jv-wave { display: none; }
  .jv .jv-card { border-color: CanvasText; }
  .jv .jv-card .jv-later::before { forced-color-adjust: none; background: ButtonText; }
  .jv-box { border-color: CanvasText; }
  .jv-box::before, .jv-box::after, .jv-stage::before { display: none; }
  .jv-transcript .is-now .jv-line { outline: 2px solid Highlight; outline-offset: -2px; }
  .jv-progress { border: 1px solid CanvasText; }
  .jv-progress > span { forced-color-adjust: none; background: CanvasText; }
}
@media print { .jv, .jv-frame { display: none !important; } }
