/* Overlay-only styling: the guided-meditation player, the panchang and
   festival sheets, the daily reading, and the games. None of it paints in the
   first frame, all of it is behind a tap, so it loads at low priority the same
   way roadmap.css and lesson-player.css do rather than blocking render.
   The Today-visible pieces (the panchang line, the festival row and the two
   extras cards) deliberately stay in styles.css. */

/* ---------- guided meditation ----------
   Concentric rings, one monochromatic brand-color family, each pulsing on its
   own offset so the whole thing breathes rather than ticks. The timer sits
   above the rings, the guidance text fades in and out at their center. */
.med-run-wrap {
  position: absolute;
  top: 0; left: 0; width: 100%; height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: calc(env(safe-area-inset-top) + 24px) 20px calc(env(safe-area-inset-bottom) + 40px);
}
.med-run-top {
  display: flex;
  justify-content: center;
  z-index: 2;
}
.med-run-timer {
  font-size: 2rem;
  font-weight: 900;
  color: #fff;
  font-variant-numeric: tabular-nums;   /* the timer must not jitter as digits change */
  letter-spacing: .02em;
}

.med-orbit {
  position: relative;
  flex: none;
  width: 264px;
  height: 264px;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  margin: auto;
}
.med-progress,
.med-run-actions { flex: none; z-index: 2; }
.med-ring {
  position: absolute;
  left: 50%; top: 50%;
  border-radius: 50%;
  border: none;
  background: var(--teal-bg, #52a38b);
  animation: medBreathe 8s ease-in-out infinite;
  z-index: -1;
  pointer-events: none;
}
.med-ring-4 { width: 160vmax; height: 160vmax; margin: -80vmax 0 0 -80vmax; opacity: 0.15; animation-delay: -4s; }
.med-ring-3 { width: 120vmax; height: 120vmax; margin: -60vmax 0 0 -60vmax; opacity: 0.30; animation-delay: -2s; }
.med-ring-2 { width: 85vmax;  height: 85vmax;  margin: -42.5vmax 0 0 -42.5vmax; opacity: 0.50; animation-delay: 0s; }
.med-ring-1 { width: 55vmax;  height: 55vmax;  margin: -27.5vmax 0 0 -27.5vmax; opacity: 0.70; animation-delay: -1s; }
.med-core {
  position: absolute;
  left: 50%; top: 50%;
  width: 32vmax; height: 32vmax; margin: -16vmax 0 0 -16vmax;
  border-radius: 50%;
  background: var(--teal-bg, #52a38b);
  box-shadow: 0 0 50px -10px var(--teal-bg, #52a38b);
  animation: medBreathe 8s ease-in-out infinite;
  z-index: -1;
  pointer-events: none;
}
.med-orbit.is-paused .med-ring,
.med-orbit.is-paused .med-core { animation-play-state: paused; }
.med-orbit.is-paused .med-core { opacity: .72; }

/* Custom Overrides for Silence and Breathwork */
.med-run-silence .med-ring,
.med-run-silence .med-core {
  background: var(--slate-bg, #61788a);
  box-shadow: 0 0 50px -10px var(--slate-bg, #61788a);
  animation-duration: 16s; /* Slower, deeper pulse for stillness */
}
.med-run-breath .med-ring,
.med-run-breath .med-core {
  background: var(--mint-bg, #63b365);
  box-shadow: 0 0 50px -10px var(--mint-bg, #63b365);
  animation: none; /* JS handles dynamic breathing scaling */
}
@keyframes medBreathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.07); }
}
@keyframes medRingPulse {
  0%, 100% { transform: scale(1);    opacity: var(--ring-o, 1); }
  50%      { transform: scale(1.09); opacity: calc(var(--ring-o, 1) * .6); }
}

.med-line {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 18px;
  font-size: 1.5rem;
  line-height: 1.4;
  font-weight: 700;
  color: #fff;
  max-width: 100%;
  margin: 0;
  transition: opacity .5s ease;
}
.med-progress {
  width: min(280px, 80%);
  height: 4px;
  margin: 0 auto;
  border-radius: var(--radius-round);
  background: rgba(255, 255, 255, 0.25);
  overflow: hidden;
}
.med-progress > span {
  display: block;
  height: 100%;
  width: 0;
  background: #fff;
  border-radius: var(--radius-round);
}
.med-run-actions {
  display: flex;
  justify-content: center;
  gap: 12px;
  margin-top: 24px;
}
.med-run-actions .btn-ghost {
  color: #fff;
  font-size: 1rem;
  font-weight: 600;
  background: rgba(255, 255, 255, 0.15);
  border-radius: 20px;
}

/* ---------- the payoff ----------
   A diya lighting, not confetti. The reward stays inside the world of the
   product, and it is the same flame the streak and onboarding already use. */
.med-celebrate { text-align: center; padding: 40px 16px 28px; position: relative; }
.med-cel-rays {
  position: absolute;
  top: 46px; left: 50%;
  width: 260px; height: 260px;
  margin-left: -130px;
  border-radius: 50%;
  background: conic-gradient(from 0deg, transparent 0 8deg, rgba(247,98,18,.20) 8deg 12deg, transparent 12deg 30deg);
  animation: medRays 9s linear infinite, medRaysIn .7s var(--ease-out, ease-out) both;
  pointer-events: none;
}
@keyframes medRays { to { transform: rotate(360deg); } }
@keyframes medRaysIn { from { opacity: 0; transform: scale(.6); } to { opacity: 1; transform: scale(1); } }

.med-cel-diya { position: relative; width: 132px; height: 132px; margin: 0 auto 18px; }
.med-cel-bowl {
  position: absolute; bottom: 0; left: 50%;
  width: 118px; height: 52px; margin-left: -59px;
  background: linear-gradient(180deg, #A55A22, #6B3410);
  border-radius: 0 0 60px 60px / 0 0 46px 46px;
  animation: medPop .5s var(--ease-out, ease-out) both;
}
.med-cel-flame {
  position: absolute; bottom: 44px; left: 50%;
  width: 30px; height: 52px; margin-left: -15px;
  background: radial-gradient(ellipse at 50% 75%, #FFF3C4 0%, #FFC24D 38%, #F76212 70%, rgba(247,98,18,0) 100%);
  border-radius: 50% 50% 50% 50% / 62% 62% 38% 38%;
  transform-origin: 50% 100%;
  animation: medPop .5s .12s var(--ease-out, ease-out) both, medFlicker 2.4s ease-in-out .6s infinite;
  filter: drop-shadow(0 0 18px rgba(247,98,18,.75));
}
@keyframes medPop { from { opacity: 0; transform: scale(.4) translateY(10px); } to { opacity: 1; transform: scale(1) translateY(0); } }
@keyframes medFlicker {
  0%, 100% { transform: scale(1) rotate(-1deg); }
  50%      { transform: scale(1.07) rotate(1.5deg); }
}
.med-cel-title {
  font-family: var(--font-title);
  font-size: 1.4375rem; font-weight: 900;
  color: var(--text-main);
  margin: 10px 0 4px;
  animation: medPop .45s .3s var(--ease-out, ease-out) both;
}
.med-cel-sub {
  font-size: 0.875rem; color: var(--text-muted); margin: 0;
  animation: medPop .45s .4s var(--ease-out, ease-out) both;
}
/* Where the practice comes from. Every meditation carries this line in the data
   and none of them showed it anywhere. It lands here, after the sit rather than
   before it, so it reads as something earned instead of something to study. */
.med-cel-tradition {
  font-size: 0.84375rem; line-height: 1.6;
  color: var(--text-muted);
  max-width: 34ch; margin: 16px auto 0;
  padding-top: 14px; border-top: 1px solid var(--border);
  animation: medPop .45s .55s var(--ease-out, ease-out) both;
}

@media (prefers-reduced-motion: reduce) {
  .med-ring, .med-core, .med-cel-rays, .med-cel-flame { animation: none; }
  .med-cel-bowl, .med-cel-title, .med-cel-sub { animation: none; opacity: 1; transform: none; }
}

/* ---------- daily reading ---------- */
/* Top padding clears .detail-top's floating back button (position:absolute
   over the content, ~64px including its own safe-area padding — see
   styles.css) — this screen has no hero image of its own to push content
   below it for free the way festival/course detail screens do, so without
   this the setup form's own "Your birth star" heading rendered directly
   underneath/behind the back button. Same fix, same reasoning, as
   .fcal-wrap's own top padding. */
/* Gold identity for the whole Reading/horoscope/kundli surface — the same
   #e0a938 the Calendar hub's own "Reading" tile uses (THEME_COLORS.gold in
   app.js), so opening the tile carries you into a page that's visibly the
   same place rather than dropping you into the app's default orange again.
   Scoped as a custom property rather than overriding --brand-primary
   globally, since this page still uses .btn-primary/.jyo-input elsewhere
   for plain, non-branded actions. */
.jyo-wrap { padding: calc(env(safe-area-inset-top, 0px) + 64px) 2px 30px; --jyo-accent: #e0a938; }
.jyo-head { text-align: center; padding: 22px 16px 24px; border-radius: var(--radius-lg); margin-bottom: 22px; }
/* The three qualities are tinted, not coloured in traffic-light red and green.
   A "careful" day is a day to consolidate, not a warning. */
.jyo-favourable { background: rgba(46,125,83,.10); }
.jyo-mixed      { background: var(--surface-alt); }
.jyo-careful    { background: rgba(197,138,42,.12); }
.jyo-kicker {
  font-size: 0.71875rem; font-weight: 800; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--text-muted);
}
.jyo-tara {
  font-family: var(--font-title);
  font-size: 2.125rem; font-weight: 900; letter-spacing: -.6px;
  color: var(--text-main); margin: 8px 0 2px;
}
.jyo-gloss { font-size: 0.90625rem; color: var(--text-muted); margin: 0; }
.jyo-body { margin-bottom: 22px; }
.jyo-lead { font-size: 1.0625rem; line-height: 1.62; font-weight: 600; color: var(--text-main); margin: 0 0 12px; }
.jyo-mean { font-size: 0.9375rem; line-height: 1.62; color: var(--text-muted); margin: 0; }

.jyo-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 4px 16px; margin-bottom: 24px;
}
.jyo-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 0; border-bottom: 1px solid var(--border);
}
.jyo-row:last-child { border-bottom: none; }
.jyo-row span { font-size: 0.8125rem; font-weight: 700; color: var(--text-muted); }
.jyo-row b { font-size: 0.96875rem; font-weight: 800; color: var(--text-main); }

.jyo-sec { margin-bottom: 22px; }
.jyo-sec h3 { font-family: var(--font-title); font-size: 1.125rem; font-weight: 900; color: var(--text-main); margin: 0 0 4px; }
.jyo-attr { font-size: 0.78125rem; font-weight: 700; color: var(--jyo-accent, var(--brand-primary)); margin: 0 0 8px; }
.jyo-sec p { font-size: 0.9375rem; line-height: 1.62; color: var(--text-muted); margin: 0; }
.jyo-caveat {
  font-size: 0.78125rem; line-height: 1.55; color: var(--text-muted); opacity: .85;
  padding-top: 14px; margin-top: 16px; border-top: 1px solid var(--border);
}
/* The button into the full birth chart. Symmetric breathing room: without the
   bottom margin it sat jammed against the next section's heading, which read as
   the button being mis-centred rather than merely crowded. Lives here (not in
   kundli.css) because it sits on the READING screen, whose styles load up front;
   kundli.css only loads once the chart is opened. */
.jyo-kundli-btn { width: 100%; margin: 10px 0 26px; background: var(--jyo-accent, var(--gradient-sunset)); }

/* Three essentials as a strip of stat tiles instead of a six-row table. Flex so
   two tiles (no lagna yet) still fill the width the way three do. */
.jyo-tiles { display: flex; gap: 8px; margin-bottom: 18px; }
.jyo-tile {
  flex: 1; min-width: 0; text-align: center;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 12px 8px;
}
.jyo-tile-label {
  display: block; font-size: 0.65625rem; font-weight: 800; letter-spacing: .5px;
  text-transform: uppercase; color: var(--text-muted); margin-bottom: 5px;
}
.jyo-tile-val {
  display: block; font-family: var(--font-title); font-size: 0.9375rem; font-weight: 900;
  color: var(--text-main); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.jyo-lead { font-size: 1.0625rem; line-height: 1.6; font-weight: 600; color: var(--text-main); margin: 0 0 4px; }

/* Collapsible rows: the teaching prose folds away by default so the screen reads
   as a short list, not an essay (see jyoFold in app.js). */
.jyo-fold {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; margin-bottom: 10px; overflow: hidden;
}
.jyo-fold-sum {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 16px;
}
.jyo-fold-sum::-webkit-details-marker { display: none; }
.jyo-fold-tt { min-width: 0; }
.jyo-fold-title { display: block; font-family: var(--font-title); font-size: 0.96875rem; font-weight: 800; color: var(--text-main); }
.jyo-fold-teaser { display: block; font-size: 0.8125rem; color: var(--text-muted); margin-top: 3px; }
.jyo-fold-chev { flex: 0 0 auto; display: flex; color: var(--text-muted); transition: transform .2s ease; }
.jyo-fold[open] .jyo-fold-chev { transform: rotate(90deg); }
.jyo-fold-body { padding: 2px 16px 16px; }
.jyo-fold-body p { font-size: 0.90625rem; line-height: 1.62; color: var(--text-muted); margin: 0 0 10px; }
.jyo-fold-body p:last-child { margin-bottom: 0; }
/* The "About this reading" fold is a footnote, not a card. */
.jyo-fold.is-quiet { background: transparent; border-color: transparent; margin-top: 4px; }
.jyo-fold.is-quiet .jyo-fold-sum { padding-left: 2px; padding-right: 2px; }
.jyo-fold.is-quiet .jyo-fold-title { font-family: var(--font-sans); font-size: 0.8125rem; font-weight: 700; color: var(--text-muted); }
.jyo-fold.is-quiet .jyo-fold-body { padding-left: 2px; padding-right: 2px; }

/* Attribute chips, the same visual language as the grahas table's dignity tags. */
.jyo-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.jyo-chip {
  font-size: 0.75rem; font-weight: 700; color: var(--text-muted);
  background: var(--surface-alt); border-radius: 8px; padding: 3px 9px;
}

.jyo-setup-head { margin-bottom: 22px; }
.jyo-setup-head h1 { font-family: var(--font-title); font-size: 1.6875rem; font-weight: 900; margin: 0 0 8px; color: var(--text-main); }
.jyo-setup-head p { font-size: 0.9375rem; line-height: 1.62; color: var(--text-muted); margin: 0; }
.jyo-form { display: flex; flex-direction: column; }
.jyo-label { font-size: 0.8125rem; font-weight: 800; color: var(--text-main); margin: 14px 0 6px; }
.jyo-label span { font-weight: 600; color: var(--text-muted); }
.jyo-input {
  padding: 14px 14px; border: 1px solid var(--border); border-radius: 14px;
  background: var(--surface); color: var(--text-main);
  font-family: var(--font-sans); font-size: 1rem; font-weight: 700;
}
.jyo-input:focus { outline: none; border-color: var(--jyo-accent, var(--brand-primary)); }
.jyo-privacy { font-size: 0.78125rem; line-height: 1.5; color: var(--text-muted); margin: 14px 0 0; }

/* ---------- games hub ---------- */
.ghub-wrap { padding: 6px 2px 30px; }
.ghub-head { margin-bottom: 20px; }
.ghub-head h1 { font-family: var(--font-title); font-size: 1.6875rem; font-weight: 900; margin: 0 0 6px; color: var(--text-main); }
.ghub-head p { font-size: 0.9375rem; line-height: 1.6; color: var(--text-muted); margin: 0; max-width: 42ch; }
.ghub-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.ghub-card {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  padding: 15px 14px; min-height: 132px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  cursor: pointer; font-family: var(--font-sans); text-align: left;
}
.ghub-icon {
  width: 40px; height: 40px; border-radius: 12px; margin-bottom: 6px;
  display: flex; align-items: center; justify-content: center; color: #fff;
}
.ghub-icon .material-symbols-rounded { font-size: 1.375rem; }
.ghub-title { font-size: 0.9375rem; font-weight: 800; color: var(--text-main); line-height: 1.25; }
.ghub-desc { font-size: 0.78125rem; font-weight: 600; color: var(--text-muted); line-height: 1.4; }
.ghub-best {
  margin-top: auto; padding-top: 6px;
  font-size: 0.71875rem; font-weight: 800; color: var(--brand-primary);
}

/* Practice tab only: the sadhana card is otherwise the same .feature-card
   every other tab uses at its default 140px. Fixed rather than viewport-fit
   (see the git history on this block for the flex:1-to-the-bottom-nav
   version — three rounds of tuning it never stopped looking like it was
   guessing) — a plain, slightly taller-than-default hero that reads as
   deliberate instead of stretched. */
#pr-sadhana-card { min-height: 172px; }

/* ---------- Mindfulness: 3 fixed-height rectangular buttons. Fixed, not
   flex-grown to fill whatever's left on the page — the same reasoning as
   #pr-sadhana-card above. A visible surface/border earns the word "button"
   at this size. ---------- */
.mindful-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.mindful-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
  height: 175px;
  border-radius: 20px; border: none;
  padding: 12px 8px; cursor: pointer;
  font-family: var(--font-sans); text-align: center;
  color: #fff;
}
.mindful-icon {
  width: 52px; height: 52px; border-radius: 50%;
  background: rgba(255, 255, 255, 0.25); color: #fff;
  display: flex; align-items: center; justify-content: center;
}
.mindful-icon .material-symbols-rounded { font-size: 1.5rem; }
.mindful-title { font-size: 0.84375rem; font-weight: 800; line-height: 1.25; }
.mindful-sub { font-size: 0.6875rem; font-weight: 600; color: rgba(255, 255, 255, 0.75); }

/* ---------- Games shelf: a horizontal row of plain icon+label tiles on
   whitespace, replacing the 2-column grid of bordered .ghub-cards (still
   above, unused now) — the same "on whitespace" treatment as Mindfulness,
   just scrolling instead of wrapping since there are more than 3 of them. ---------- */
.game-shelf-item {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  width: 96px; flex: 0 0 auto; scroll-snap-align: start;
  background: transparent; border: none;
  font-family: var(--font-sans); cursor: pointer; text-align: center;
}
.game-shelf-icon {
  width: 88px; height: 88px; border-radius: 24px; color: #fff;
  display: flex; align-items: center; justify-content: center;
}
.game-shelf-icon .material-symbols-rounded { font-size: 2.125rem; }
.game-shelf-icon svg { width: 46px; height: 46px; }
/* The bare K+ mark floated straight over the tile's rounded corner and read as
   a stray sticker. A coin behind it, bordered in the page's own background,
   reads instead as a notch punched into the tile — the border has to be
   --bg-app (not a fixed white) since the Games shelf sits on that background
   in both themes. */
.game-shelf-lock {
  position: absolute; top: -7px; right: -7px;
  width: 30px; height: 30px; border-radius: 50%;
  background: linear-gradient(135deg, #FFEFB0, #D4A852, #B28833);
  border: 3px solid var(--bg-app);
  display: flex; align-items: center; justify-content: center;
}
.game-shelf-lock .plus-mark-dark { width: 15px; height: 15px; }
.game-shelf-title { font-size: 0.78125rem; font-weight: 800; color: var(--text-main); line-height: 1.25; }
.game-shelf-best { font-size: 0.65625rem; font-weight: 800; color: var(--brand-primary); }

/* ---------- festival calendar (sheet only) ----------
   A real month grid, not a list. A calendar exists to show the SHAPE of a
   month: which week something lands in, how many clear days sit between two
   festivals, whether one falls on a weekend. A list cannot show any of that.
   Top padding clears .detail-top's floating back button (position:absolute,
   its own safe-area-aware padding — see styles.css) — the hero-based detail
   screens (festival/course) get this for free from .detail-hero's own top
   padding; this screen has no hero, so without it the calendar's first row
   opened directly underneath/behind the back button. calc() mirrors
   .detail-hero's own "safe-area + 64px" exactly, for the same reason: that
   64px is sized to the back button's height + its resting offset, not to
   this screen's content. "Your reading" used to be a full-width row here
   too (see fcal-day-detail below for where that entry point lives now, as
   a header icon instead) — removing it was the other half of making this
   screen fit a phone screen without scrolling past the fold immediately. */
.fcal-wrap { padding: calc(env(safe-area-inset-top, 0px) + 64px) 2px 30px; }

/* ---------- calendar hub (the 3-tile landing this screen opens on) ----------
   Fills #detail-body edge to edge with NO scroll of its own — the opposite of
   every other .detail-body screen, which scroll freely and just pad their own
   top clear of the floating back button. Doing that here means #detail-body
   itself has to stop being a scroll container while this is showing (see
   .detail-body.fcal-hub-layout, toggled in app.js), and this wrapper has to
   claim the FULL height that leaves behind rather than sizing to its content
   the way .fcal-wrap above does. The big Calendar tile is a true square
   (aspect-ratio, not a fixed height) — realistic phone dimensions leave
   comfortable room below it for the reading/custom row at that width, so this
   doesn't fight the "must not scroll" rule, but it IS a screen-width
   assumption rather than a guaranteed one. */
.detail-body.fcal-hub-layout {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Overrides the base .detail-body's own padding (22px sides, 120px bottom
     for bottom-nav clearance) — there's no bottom nav to clear behind this
     full-screen overlay, and giving that space back is the difference
     between the tiles actually filling the screen and just filling most of
     it. .fcal-hub (below) handles its own edge padding instead. */
  padding: 0;
}
.fcal-hub {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* Same 16px on every side but the top, which still needs the back button's
     own safe-area + 64px clearance — that asymmetry is the ONE deliberate
     exception, not a stand-in for consistent side/bottom margins the way the
     old 2px sides accidentally were. */
  padding: calc(env(safe-area-inset-top, 0px) + 64px) 16px calc(env(safe-area-inset-bottom, 0px) + 16px);
}
.fcal-hub-tile {
  position: relative;
  overflow: hidden;
  border: none;
  border-radius: 22px;
  padding: 18px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-start;
  text-align: left;
  color: #fff;
  cursor: pointer;
  font-family: inherit;
}
.fcal-hub-tile:active { transform: scale(.98); }
/* The one big square tile — sized by its own width (the row below just takes
   whatever's left), not by flex-grow, so it reads as a deliberate square
   rather than "however much space happened to be free". Its content runs
   top-to-bottom (icon+title, then the upcoming list, then the watermark
   underneath all of it) rather than everything pinned to the bottom the way
   the two small tiles are — there's room here to actually show something,
   not just a label. */
.fcal-hub-calendar {
  flex: 0 0 auto;
  width: 100%;
  aspect-ratio: 1 / 1;
  justify-content: flex-start;
}
.fcal-hub-row {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: 14px;
}
.fcal-hub-row .fcal-hub-tile { flex: 1; min-width: 0; }
.fcal-hub-watermark {
  position: absolute;
  right: -18px;
  bottom: -24px;
  font-size: 8.125rem;
  line-height: 1;
  opacity: .22;
  transform: rotate(18deg);
  pointer-events: none;
  z-index: 0;
}
.fcal-hub-watermark .material-symbols-rounded { font-size: inherit; }
.fcal-hub-title {
  font-family: var(--font-title);
  font-size: 1.1875rem;
  font-weight: 850;
  letter-spacing: -0.2px;
  position: relative;
  z-index: 1;
}
.fcal-hub-sub {
  font-size: 0.78125rem;
  font-weight: 800;
  color: rgba(255,255,255,.95);
  background: rgba(0,0,0,.18);
  padding: 3px 9px;
  border-radius: 8px;
  position: relative;
  z-index: 1;
  margin-top: 6px;
}
/* The big tile's own header row (a small badge icon, not the giant bleeding
   watermark the two small tiles use — there isn't room left for both a
   readable list AND a 220px icon) plus the upcoming-festivals preview below
   it. Both sit above the watermark (z-index 1) and above the color-mixed
   overlay .fcal-hub-calendar's ::before could otherwise use for legibility. */
.fcal-hub-calendar-head {
  display: flex;
  align-items: center;
  gap: 10px;
  position: relative;
  z-index: 1;
  margin-bottom: 14px;
}
.fcal-hub-calendar-icon {
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  background: rgba(255,255,255,.22);
  display: flex;
  align-items: center;
  justify-content: center;
}
.fcal-hub-calendar-icon .material-symbols-rounded { font-size: 1.1875rem; }
.fcal-hub-calendar .fcal-hub-title { font-size: 1.375rem; }
.fcal-hub-upcoming {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 9px;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}
.fcal-hub-upcoming-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.fcal-hub-upcoming-name {
  flex: 1;
  min-width: 0;
  font-size: 0.875rem;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fcal-hub-upcoming-when {
  flex: 0 0 auto;
  font-size: 0.71875rem;
  font-weight: 700;
  color: rgba(255,255,255,.8);
}

.fcal-nav { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 14px; }
.fcal-arrow {
  width: 40px; height: 40px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: var(--radius-round);
  background: var(--surface); color: var(--text-main); cursor: pointer;
}
.fcal-title {
  flex: 1; text-align: center;
  font-family: var(--font-title); font-size: 1.1875rem; font-weight: 900;
  color: var(--text-main); letter-spacing: -.3px;
}
.fcal-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 6px; }
.fcal-dow span { text-align: center; font-size: 0.6875rem; font-weight: 800; letter-spacing: .6px; color: var(--text-muted); }
.fcal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.fcal-cell {
  aspect-ratio: 1 / 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  border: none; background: none; border-radius: 11px; padding: 0;
  font-family: var(--font-sans); cursor: default;
}
.fcal-cell.is-blank { visibility: hidden; }
.fcal-num { font-size: 0.90625rem; font-weight: 700; color: var(--text-main); font-variant-numeric: tabular-nums; }
/* A festival day is filled rather than merely dotted, so the month reads at a
   glance; the dots carry the count when several fall on one date. */
.fcal-cell.has-fest { background: var(--brand-primary-light); cursor: pointer; }
.fcal-cell.has-fest .fcal-num { color: var(--brand-primary); font-weight: 900; }
.fcal-dots { display: flex; gap: 2px; height: 4px; }
.fcal-dots i { width: 4px; height: 4px; border-radius: 50%; background: var(--brand-primary); }
.fcal-cell.is-today { box-shadow: inset 0 0 0 2px var(--brand-primary); }
.fcal-cell.is-past { opacity: .38; }
.fcal-cell.is-past.has-fest { opacity: .55; }
/* The tapped day, its detail panel open below (.fcal-day-detail) — a
   filled brand-primary cell reads as "this one's open" the same way
   is-today's ring reads as "this one's now," at a glance rather than
   needing to scroll down to confirm which day the panel belongs to. */
.fcal-cell.is-selected { background: var(--brand-primary); }
.fcal-cell.is-selected .fcal-num { color: #fff; }
.fcal-cell.is-selected .fcal-dots i { background: #fff; }
/* Names sit under the grid: at phone width they cannot go inside a cell without
   shrinking the dates past readability. */
.fcal-list { margin-top: 20px; display: flex; flex-direction: column; }
.fcal-item {
  display: flex; align-items: center; gap: 14px;
  width: 100%; padding: 11px 4px; text-align: left;
  background: none; border: none; border-top: 1px solid var(--border);
  font-family: var(--font-sans); cursor: pointer;
}
.fcal-item-day { flex: 0 0 30px; text-align: center; font-size: 1rem; font-weight: 900; color: var(--brand-primary); font-variant-numeric: tabular-nums; }
.fcal-item-name { flex: 1; min-width: 0; font-size: 0.96875rem; font-weight: 700; color: var(--text-main); }
.fcal-item-when { font-size: 0.78125rem; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.fcal-item.is-past { opacity: .45; }
.fcal-empty { margin-top: 22px; font-size: 0.90625rem; color: var(--text-muted); text-align: center; }

/* A tapped day's events, replacing .fcal-list in place (same slot below the
   grid) rather than navigating away — Read more is the only thing that
   actually leaves this screen. */
.fcal-day-detail { margin-top: 20px; }
.fcal-day-detail-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px;
}
.fcal-day-detail-head h3 {
  font-family: var(--font-title); font-size: 1.0625rem; font-weight: 800;
  color: var(--text-main); margin: 0; letter-spacing: -.2px;
}
.fcal-day-detail-close {
  width: 32px; height: 32px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%; background: var(--surface-alt);
  color: var(--text-muted); cursor: pointer;
}
.fcal-day-event {
  display: flex; align-items: center; gap: 12px;
  padding: 14px; margin-bottom: 10px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 16px;
}
.fcal-day-event-icon {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  background: var(--brand-primary-light); color: var(--brand-primary);
}
.fcal-day-event-text { flex: 1; min-width: 0; }
.fcal-day-event-name { font-size: 0.9375rem; font-weight: 800; color: var(--text-main); }
.fcal-day-event-when { font-size: 0.78125rem; font-weight: 600; color: var(--text-muted); margin-top: 1px; }
.fcal-day-event-more {
  flex: 0 0 auto; display: flex; align-items: center; gap: 2px;
  border: none; background: none; padding: 0;
  font-family: var(--font-sans); font-size: 0.8125rem; font-weight: 800;
  color: var(--brand-primary); cursor: pointer;
}
.fcal-day-event-more .material-symbols-rounded { font-size: 1.125rem; }

/* ---- the personal layer ----------------------------------------------------
   Festivals are orange because they belong to everybody. A user's own days need
   a second accent, or a month with Ekadashi, Pradosham and Purnima in it is
   half-filled in one colour and the grid stops reading at a glance.

   That accent is the mint pair and not the olive one, for a reason worth
   writing down. --secondary/-light was the obvious choice and it is unreadable
   in dark mode: the theme resolves them to #4A4642 on #322F2C, about 1.2:1, so
   every personal day vanished into the background. --success-ink on
   --success-light is the token pair the theme already flips for both schemes
   (#04785C on #DFF8EF light, #7CEBC9 on #0E3A2E dark), which is why it is used
   here rather than a hand-picked colour that would need its own dark variant. */
.fcal-cell.is-mine { background: var(--success-light); }
.fcal-cell.is-mine .fcal-num { color: var(--success-ink); font-weight: 900; }
.fcal-dots i.is-personal { background: var(--success-ink); }
.fcal-cell.is-selected .fcal-dots i.is-personal { background: #fff; }
.fcal-item.is-personal .fcal-item-day { color: var(--success-ink); }
.fcal-day-event.is-personal .fcal-day-event-icon { background: var(--success-light); color: var(--success-ink); }
/* Why this day and not the one next to it. Shown whenever the tradition had to
   break a tie, because the person keeping the fast is the one who should know
   that it was broken. */
.fcal-day-event-why {
  font-size: 0.75rem; line-height: 1.45; font-weight: 500;
  color: var(--text-muted); margin-top: 5px;
}

/* ---- calendar setup ---- */
/* Top padding clears .detail-top's floating back button, which is absolutely
   positioned over the content. This screen has no hero image to push content
   down for free, so without it the first paragraph renders underneath the
   button. Same fix and same reasoning as .jyo-wrap and .fcal-wrap. */
/* Purple identity for the personal calendar setup — the same #9b72cf the
   Calendar hub's own "Your calendar" tile uses (THEME_COLORS.purple in
   app.js), matching .jyo-wrap's own --jyo-accent treatment right above for
   the same reason: the tile you tapped should carry its color in with it. */
.cal-setup { padding: calc(env(safe-area-inset-top, 0px) + 64px) 2px 40px; --cal-accent: #9b72cf; }
.cal-setup .jyo-input:focus { border-color: var(--cal-accent); }
.cal-setup .btn-primary { background: var(--cal-accent); }
.cal-setup .cal-inline-btn { color: var(--cal-accent); background: color-mix(in srgb, var(--cal-accent) 14%, var(--surface-alt)); }
.cal-setup .cal-check input,
.cal-setup .cal-radio input { accent-color: var(--cal-accent); }
/* .jyo-input carries no width of its own because in the birth form it is a
   flex child that stretches. Here it is a plain block, so an input would size
   itself to its default character count and come out about 60% wide. */
.cal-setup .jyo-input { width: 100%; box-sizing: border-box; display: block; }
.cal-intro { font-size: 0.90625rem; line-height: 1.6; color: var(--text-muted); margin: 0 0 8px; }
.cal-h {
  font-family: var(--font-title); font-size: 1.0625rem; font-weight: 900;
  color: var(--text-main); letter-spacing: -.2px;
  margin: 28px 0 5px;
}
.cal-sub { font-size: 0.8125rem; line-height: 1.55; color: var(--text-muted); margin: 0 0 11px; }
.cal-sub em { font-style: normal; font-weight: 700; color: var(--text-main); }
.cal-note {
  display: flex; align-items: flex-start; gap: 7px;
  font-size: 0.78125rem; line-height: 1.5; color: var(--text-muted);
  margin: 10px 0 0;
}
.cal-note .material-symbols-rounded { font-size: 1.0625rem; flex: 0 0 auto; margin-top: -1px; }
.cal-fine { font-size: 0.71875rem; line-height: 1.55; color: var(--text-muted); margin: 14px 0 0; text-align: center; }
.cal-empty-note { font-size: 0.8125rem; color: var(--text-muted); margin: 0 0 10px; }

.cal-checks { display: flex; flex-direction: column; gap: 2px; }
.cal-check {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 11px 2px; cursor: pointer;
  border-top: 1px solid var(--border);
}
.cal-check input { margin-top: 2px; width: 19px; height: 19px; flex: 0 0 auto; accent-color: var(--brand-primary); }
.cal-check input:disabled { cursor: not-allowed; }
/* A checkbox that cannot be ticked has to look it, or the tap reads as a bug
   rather than as a missing prerequisite. */
.cal-check:has(input:disabled) { opacity: .45; cursor: not-allowed; }
.cal-check-body { min-width: 0; }
.cal-check-name { display: block; font-size: 0.90625rem; font-weight: 700; }
.cal-check-note { display: block; margin-top: 2px; font-size: 0.78125rem; line-height: 1.45; color: var(--text-muted); }

.cal-radios { display: flex; flex-direction: column; gap: 2px; }
.cal-radio {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 2px; cursor: pointer; border-top: 1px solid var(--border);
  font-size: 0.90625rem; font-weight: 600;
}
.cal-radio input { width: 19px; height: 19px; flex: 0 0 auto; accent-color: var(--brand-primary); }
.cal-radio em { font-style: normal; font-weight: 500; color: var(--text-muted); }

.cal-inline-btn {
  margin-top: 9px; padding: 9px 15px;
  background: var(--surface-alt); border: none; border-radius: 999px;
  font-family: var(--font-sans); font-size: 0.8125rem; font-weight: 700;
  color: var(--text-main); cursor: pointer;
}
.cal-fam-add { display: flex; gap: 8px; align-items: center; margin-top: 10px; flex-wrap: wrap; }
.cal-fam-add .jyo-input { flex: 1 1 120px; margin: 0; }
.cal-fam-add .cal-inline-btn { margin-top: 0; }
.cal-fam-row {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 2px; border-top: 1px solid var(--border);
}
.cal-fam-name { flex: 1; min-width: 0; font-size: 0.90625rem; font-weight: 700; }
.cal-fam-date { font-size: 0.78125rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.cal-fam-del {
  width: 30px; height: 30px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%; background: var(--surface-alt);
  color: var(--text-muted); cursor: pointer;
}
.cal-fam-del .material-symbols-rounded { font-size: 1.125rem; }

/* ===========================================================================
   Guided meditation: the picker
   ===========================================================================
   Ten practices used to render as ten <button class="list-row"> in the same
   grey, sharing three icons between them, so the eye had nothing to tell them
   apart and the list read as one thing repeated. It also inherited .list-row,
   which is a Library component; a meditation is a different kind of thing and
   should not borrow a bookmark row's clothes.

   Each practice now carries a `tone` in meditations.json and its own glyph. The
   tones are declared as PAIRS, a wash and the ink that sits on it, so a theme
   can never leave one legible and the other not. Both themes are set explicitly
   rather than derived, because these are the only saturated surfaces in the app
   and a derived dark variant of a pale wash is mud.
   =========================================================================== */

/* Sticks to the top of .detail-body's scroll instead of sitting after the
   ten meditation cards, where scrolling past every one of them was the
   only way to ever reach it. top:0 would stick it flush to the top of
   .detail-body's own box, which starts well above where the floating
   back button (.detail-top, position:absolute over .detail, safe-area
   aware) actually sits — the bar rode straight up underneath it on
   scroll. The +52px offset is the same clearance .text-reader-stickybar
   already uses for the identical reason (back button padding-top max(16,
   safe-area) + its 40px size + a little breathing room). */
.med-soundscape-sticky {
  position: sticky;
  top: calc(max(16px, env(safe-area-inset-top)) + 52px);
  z-index: 5;
  background: var(--bg-app);
  margin: 0 -4px 12px;
  padding: 4px 4px 12px;
  border-bottom: 1px solid var(--border);
}

.med-pick {
  --med-wash: var(--card-tulsi);
  --med-ink: var(--card-tulsi-text);
  -webkit-appearance: none;
          appearance: none;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 13px;
  width: 100%;
  margin: 0 0 10px;
  padding: 15px 16px;
  border: 0;
  border-radius: var(--radius-lg, 16px);
  background: var(--med-wash);
  color: var(--med-ink);
  font-family: var(--font-sans);
  text-align: left;
  cursor: pointer;
  transition: transform .16s cubic-bezier(.22, 1, .36, 1), filter .2s ease;
}
.med-pick:active { transform: scale(.985); }

/* The oversized glyph is wallpaper, not an icon: it gives each row a silhouette
   you recognise before you have read the title. currentColor so it follows the
   paired ink and cannot go invisible in either theme. */
.med-pick-wm {
  position: absolute;
  right: -14px;
  bottom: -22px;
  opacity: .13;
  transform: rotate(-10deg);
  pointer-events: none;
  color: currentColor;
}
.med-pick-wm .material-symbols-rounded { font-size: 4.875rem; }

.med-pick-glyph {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: 13px;
  /* A tint of the ink rather than a fixed colour, so the chip sits on the wash
     in both themes without a second token. */
  background: color-mix(in srgb, currentColor 15%, transparent);
  color: currentColor;
}
.med-pick-glyph .material-symbols-rounded { font-size: 1.375rem; }

.med-pick-body { position: relative; flex: 1; min-width: 0; }
.med-pick-title {
  display: block;
  font-family: var(--font-title, inherit);
  font-size: 0.96875rem;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -.2px;
}
/* No opacity on the subtitle: these washes are already low-contrast and fading
   the ink pushes it under 4.5:1. Hierarchy is size and weight only. */
.med-pick-sub {
  display: block;
  margin-top: 2px;
  font-size: 0.78125rem;
  font-weight: 600;
  line-height: 1.4;
}

.med-pick-min {
  position: relative;
  flex: 0 0 auto;
  align-self: flex-start;
  padding-top: 2px;
  font-size: 1.0625rem;
  font-weight: 900;
  line-height: 1;
  white-space: nowrap;
  text-align: center;
}
.med-pick-min small { display: block; font-size: 0.65625rem; font-weight: 700; margin-top: 2px; }

/* ---- the tones ----
   Using the app's core pastel palette for saturated button backgrounds,
   with solid white ink for maximum contrast in both light and dark modes. */
.med-tone-dawn   { --med-wash: var(--sand-bg, #e8bc7d); --med-ink: #fff; }
.med-tone-river  { --med-wash: var(--sky-bg, #6c9db2);  --med-ink: #fff; }
.med-tone-flame  { --med-wash: var(--rose-bg, #d96b83); --med-ink: #fff; }
.med-tone-amber  { --med-wash: var(--mint-bg, #63b365); --med-ink: #fff; }
.med-tone-rose   { --med-wash: var(--teal-bg, #52a38b); --med-ink: #fff; }
.med-tone-indigo { --med-wash: var(--slate-bg, #61788a); --med-ink: #fff; }
.med-tone-dusk   { --med-wash: #8675a9;                 --med-ink: #fff; }

@media (prefers-reduced-motion: reduce) {
  .med-pick { transition: none; }
  .med-pick:active { transform: none; }
}

/* The painted cover, once web/meditations-image-prompts.txt has been run through
   the GPU and the webp files are in the bucket. Absent until then: the <img>
   404s, JS removes it, and the card keeps its flat tone with no gap. Sits under
   the content and over the wash, dimmed so the title stays legible on top of
   whatever the painting happens to be doing in that corner. */
.med-pick-art {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: .34;
  pointer-events: none;
  /* Fades toward the left so the glyph chip and the text always sit on the
     plain wash rather than on the busiest part of the image. */
  -webkit-mask-image: linear-gradient(to right, transparent 0%, transparent 34%, #000 100%);
          mask-image: linear-gradient(to right, transparent 0%, transparent 34%, #000 100%);
}
body.dark-mode .med-pick-art { opacity: .42; }

/* A Plus meditation still shows in full colour and stays tappable, because the
   tap is what opens the pricing sheet. The badge is so the state is legible
   BEFORE the tap: a card that looks free and then refuses reads as a trick. */
/* IN THE FLOW, not absolutely positioned. Pinned to the top right it landed
   exactly on the minutes, so every card read "min" with the number hidden under
   a gold circle. It now sits in the row as its own column, before the duration. */
.med-pick-plus {
  position: relative;
  flex: 0 0 auto;
  align-self: flex-start;
  margin-top: 1px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: linear-gradient(150deg, #F6D678 0%, #D99A2B 100%);
  color: #4A2F00;
  box-shadow: 0 1px 4px rgba(180, 130, 20, .4);
}
.med-pick-plus .material-symbols-rounded { font-size: 0.875rem; }

/* ============================================================
   Sadhana builder: full-screen journey (swb-)
   Same shell pattern as the lesson player's .slp-overlay — dynamically
   created/destroyed, slides in from the right. See app.js's
   openSadhanaBuilder/renderSwb/goToSwbPanel. Content inside each panel
   (progress dots, preset cards, accordions, review list) is the .sw-* CSS
   in styles.css, unchanged by this shell.

   Panels slide via a plain CSS transition on a JS-set transform
   (goToSwbPanel, in app.js) — driven only by explicit navigation
   (Back/Next, step 1's choice cards), never by touch-drag. Two earlier
   versions of THAT (scroll-snap, then a percentage-height sliding track a
   user could drag) both turned out to fight the browser's own
   scroll-into-view behavior in ways that were hard to fully pin down, so
   this keeps the slide but drops the drag input that kept causing it —
   still no shared scroll container spanning multiple panels: each panel's
   own .swb-panel-scroll below is an independent overflow-y:auto region.
   Off-screen panels get `inert` (see goToSwbPanel) instead of
   `display:none`, so there's something for the transform to visibly slide
   in/out, but they're still unfocusable and inert to pointer events. The
   review panel's own confirm gesture (.swb-confirm-sheet, below) is a
   real, deliberate swipe, pinned outside .swb-panels and unrelated to any
   of this.
   ============================================================ */
.swb-overlay {
  position: absolute;
  inset: 0;
  z-index: 400;
  background: var(--bg-app);
  transform: translateX(100%);
  pointer-events: none;
  transition: transform 420ms cubic-bezier(0.22, 1, 0.36, 1);
  /* The "Edit Sadhana" card on Practice is teal (see .pr-sadhana-shell); the
     builder it opens into should read as the same feature, not switch back
     to the app's default orange the moment it opens. Every builder rule
     already styles itself via var(--brand-primary)/var(--brand-primary-light)
     rather than a hardcoded hex, so overriding those two custom properties
     here is enough to retint every button, chip, selected state and badge
     inside this overlay — nothing outside .swb-overlay is affected, since a
     CSS custom property only cascades to descendants. The two "Sadhana
     Built!" showCelebration() calls are the one exception: that overlay is
     a separate top-level element outside this subtree, so those hardcode
     the same teal by hand instead (see openSadhanaBuilder/quickBuildSadhana
     in app.js). */
  --brand-primary: #52a38b;
  --brand-primary-shadow: #3d7c68;
  --brand-primary-light: #dcefe9;
}
.swb-overlay.open { transform: translateX(0); pointer-events: auto; }
body.dark-mode .swb-overlay { --brand-primary-light: #16302a; }

.swb-top {
  position: absolute;
  top: 0; left: 0; right: 0;
  z-index: 5;
  padding: max(14px, env(safe-area-inset-top)) 16px 10px;
  pointer-events: none;
}
.swb-close {
  pointer-events: auto;
  width: 40px; height: 40px;
  border: none; border-radius: 99px;
  background: rgba(0, 0, 0, 0.35);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: transform 0.1s ease;
}
.swb-close:active { transform: scale(0.92); }
body.dark-mode .swb-close { background: rgba(255, 255, 255, 0.12); }

/* The clip viewport — overflow:hidden lives here, and this element is NEVER
   itself transformed. .swb-panel-track (below) is the one that slides; if
   the two were combined, translating the box would drag its own clip region
   along with it and clip the very panel the translate was meant to reveal
   (this shipped broken once already — a transform moves an element's
   rendered content AND its own overflow clip as one rigid unit). */
.swb-panels {
  position: relative;
  height: 100%;
  overflow: hidden;
}
.swb-panel-track {
  display: flex;
  height: 100%;
  transition: transform 0.38s cubic-bezier(0.16, 1, 0.3, 1);
}
.swb-panel {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}
/* inert (see goToSwbPanel) already drops these from the tab order and
   pointer events; belt-and-suspenders since inert support is newer than
   most of this app's baseline. */
.swb-panel[inert] { pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .swb-panel-track { transition: none; }
}
.swb-panel-scroll { flex: 1; min-height: 0; overflow-y: auto; }
.swb-panel-inner {
  padding: calc(env(safe-area-inset-top, 0px) + 74px) 22px 28px;
}
/* Step 1 lost its intention field (now just intro copy + the preset grid),
   which left a block of dead space below it on anything but the shortest
   phone. Centering it in the available height reads as a deliberately
   simple screen instead of an unfinished one; still scrolls normally if a
   short viewport ever needs it to. */
.swb-panel[data-swb-panel="0"] .swb-panel-scroll { display: flex; flex-direction: column; justify-content: center; }
.swb-panel[data-swb-panel="0"] .swb-panel-inner { flex: 0 0 auto; width: 100%; box-sizing: border-box; }
/* No per-panel entrance animation here (there used to be one) — renderSwb()
   rebuilds ALL THREE panels' DOM fresh on every interaction, not just real
   navigation, including whichever panel is currently ON SCREEN. An
   animation that starts every freshly-inserted .swb-panel-inner at
   opacity:0 meant the panel you were LOOKING AT went transparent the
   instant you tapped anything — no motion of any kind plays panel-to-panel
   now, .is-active is a plain, instant display swap. */
.swb-panel-actions {
  flex: 0 0 auto;
  padding: 14px 22px calc(20px + env(safe-area-inset-bottom, 0px));
  background: linear-gradient(to top, var(--bg-app) 60%, transparent);
}
/* The last panel has no .swb-panel-actions of its own (its "action" is the
   pinned confirm sheet below, not something living inside the panel itself)
   — pad its content so the resting sheet never covers the tail end of the
   review list. */
.swb-panel-inner-review { padding-bottom: calc(76px + env(safe-area-inset-bottom, 0px) + 24px); }

/* Bottom sheet confirm — a small rounded strip at rest; drag it up (or just
   tap it) and it grows to fill the screen in solid orange, standing in for
   the swipe-up-to-confirm a small draggable button used to be. Lives
   outside .swb-panels (see openSadhanaBuilder) so it never disappears with
   whichever panel is display:none'd. Off-screen (translateY 110%) by
   default, slid up into view only
   on the last panel via .is-visible — app.js's goToSwbPanel toggles that
   class instead of the old instant `hidden` attribute, so arriving at/
   leaving the review step now animates the sheet in/out instead of
   popping. Height (the drag-to-grow-orange gesture itself) is driven
   directly by wireSwipeToComplete's own inline styles, not this file. */
.swb-confirm-sheet {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 76px;
  z-index: 20;
  background: var(--brand-primary);
  border-radius: 24px 24px 0 0;
  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  padding-top: 10px;
  touch-action: none;
  cursor: grab;
  overflow: hidden;
  transform: translateY(110%);
  transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
  pointer-events: none;
}
.swb-confirm-sheet.is-visible {
  transform: translateY(0);
  pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) {
  .swb-confirm-sheet { transition: none; }
}
.swb-confirm-handle {
  width: 40px; height: 4px;
  border-radius: 99px;
  background: rgba(255, 255, 255, 0.55);
  flex: 0 0 auto;
}
.swb-confirm-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: #fff;
  font-family: var(--font-sans);
  font-weight: 800;
  font-size: 0.90625rem;
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
.swb-confirm-content .material-symbols-rounded {
  font-size: 1.375rem;
  animation: swbConfirmHint 1.6s ease-in-out infinite;
}
@keyframes swbConfirmHint {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) {
  .swb-confirm-content .material-symbols-rounded { animation: none; }
}

.swb-back-link {
  display: inline-flex; align-items: center; gap: 4px;
  border: none; background: transparent; padding: 4px 0; margin-bottom: 18px;
  color: var(--text-muted); font-family: var(--font-sans); font-size: 0.84375rem; font-weight: 700;
  cursor: pointer;
}
.swb-back-link .material-symbols-rounded { font-size: 1.125rem; }

/* Panel 1's intro text — replaces the old colored .detail-hero band, now
   sitting directly on the themed background instead of a separate banner. */
.swb-intro { margin-bottom: 24px; }
.swb-intro-kicker {
  font-size: 0.71875rem; font-weight: 800; letter-spacing: 1px; text-transform: uppercase;
  color: var(--brand-primary); margin-bottom: 8px;
}
.swb-intro-title {
  font-family: var(--font-title); font-size: 1.75rem; font-weight: 900;
  color: var(--text-main); margin: 0 0 8px; line-height: 1.15;
}
.swb-intro-sub { font-size: 0.875rem; line-height: 1.5; color: var(--text-muted); margin: 0; max-width: 36ch; }
