/* ------------------------------------------------------------------ polish.css
   Life365 visual layer. Loaded AFTER app.css / vastu_input.css /
   closing_panel.css and overrides by selector only -- no markup changes,
   no copy changes. Every colour is a token from app.css (:root) or a
   color-mix() of tokens, so light and dark both work with no second copy.

   app.css already asks for Fraunces (h1, h2, .k-wash) but nothing ever
   loaded it -- headings were silently falling back to Georgia. That was
   fixed here with an @import from fonts.googleapis.com, which bought the
   typeface at the price of telling Google the IP and referrer of every
   reader opening a private reading, and of making the headings wait on a
   third-party round trip over an Indian mobile connection.

   Fraunces, IBM Plex Sans and IBM Plex Mono are now on disk in app/fonts/
   and declared in app/fonts/fonts.css, which index.html loads first. The
   import is gone and the page makes no third-party request at all.         */

/* Hindi / Marathi headings: Fraunces has no Devanagari. Per-glyph fallback
   sends Devanagari to the bundled Noto Sans Devanagari while Latin keeps
   the display face, so mixed-script headlines never fall to a system serif. */
html[lang="hi"] h1, html[lang="mr"] h1,
html[lang="hi"] h2, html[lang="mr"] h2 {
  font-family: Fraunces, "Noto Sans Devanagari", Georgia, serif;
}

/* --------------------------------------------------------------- foundations */
:root {
  --pv-r-card: 14px;
  --pv-r-ctl: 10px;
  --pv-shadow-1: 0 1px 2px rgba(20, 24, 36, .05);
  --pv-shadow-2: 0 1px 2px rgba(20, 24, 36, .05), 0 8px 20px rgba(20, 24, 36, .06),
                 0 24px 48px -12px rgba(20, 24, 36, .10);
  --pv-shadow-3: 0 2px 4px rgba(34, 51, 92, .18), 0 10px 24px -6px rgba(34, 51, 92, .32);
  --pv-lift: inset 0 1px 0 rgba(255, 255, 255, .55);
}
@media (prefers-color-scheme: dark) {
  :root {
    --pv-shadow-1: 0 1px 2px rgba(0, 0, 0, .5);
    --pv-shadow-2: 0 1px 1px rgba(0, 0, 0, .55), 0 10px 24px rgba(0, 0, 0, .40),
                   0 30px 60px -14px rgba(0, 0, 0, .55);
    --pv-shadow-3: 0 2px 6px rgba(0, 0, 0, .5), 0 12px 28px -6px rgba(0, 0, 0, .6);
    --pv-lift: inset 0 1px 0 rgba(255, 255, 255, .05);
  }
}

body {
  background-color: var(--paper);
  background-image:
    radial-gradient(140% 44% at 50% 0%,
      color-mix(in srgb, var(--deep) 7%, var(--paper)) 0%,
      color-mix(in srgb, var(--deep) 2%, var(--paper)) 55%,
      var(--paper) 100%);
  background-repeat: no-repeat;
  line-height: 1.66;
}
::selection { background: var(--deep-soft); color: var(--ink); }

.wrap { padding-top: 26px; }

/* a little more room to breathe on anything wider than a phone;
   .foot must track .wrap or the tab bar detaches from the column */
@media (min-width: 720px) {
  .wrap, .foot { max-width: 520px; }
  .card { padding: 26px 28px; }
}
/* desktop: frame the reading column like a page on a desk -- two hairlines
   just outside the column, nothing else. Tokens keep it right in dark. */
@media (min-width: 900px) {
  body {
    background-image:
      linear-gradient(color-mix(in srgb, var(--line) 55%, transparent),
        color-mix(in srgb, var(--line) 55%, transparent)),
      linear-gradient(color-mix(in srgb, var(--line) 55%, transparent),
        color-mix(in srgb, var(--line) 55%, transparent)),
      radial-gradient(140% 44% at 50% 0%,
        color-mix(in srgb, var(--deep) 8%, var(--paper)) 0%,
        color-mix(in srgb, var(--deep) 2%, var(--paper)) 55%,
        var(--paper) 100%);
    background-size: 1px 100%, 1px 100%, auto;
    background-position: calc(50% - 296px) 0, calc(50% + 296px) 0, 0 0;
    background-repeat: no-repeat;
  }
}

/* ---------------------------------------------------------------- typography */
h1 {
  font-weight: 500;
  font-size: clamp(30px, 8vw, 40px);
  letter-spacing: -.02em;
  margin: 14px 0 12px;
}
h2 {
  font-weight: 500;
  font-size: 24px;
  letter-spacing: -.01em;
  line-height: 1.22;
}
p { text-wrap: pretty; }
.lead { font-size: 17.5px; line-height: 1.6; color: var(--ink); }

/* section opener: tag + rule + title reads as a chapter head, not a label */
.card > h2 { margin: 6px 0 12px; }
.card > .tag { margin-bottom: 8px; }

/* the long prose sections: give paragraphs air so 21k characters read
   as pages, not as a wall */
.card p { margin-bottom: 15px; }
.card p:last-child { margin-bottom: 0; }

/* line-item outputs. The reading is moving from prose to pointers.
   Mechanism A: newline-delimited lines inside the existing body <p>.
   pre-line keeps each line its own line; today's payload contains no
   newlines anywhere, so this changes nothing until the lines arrive. */
article.card > p { white-space: pre-line; }

/* Mechanism B: real lists. Considered markers, hanging indent, air
   between items -- these are about to be the dominant element. */
.card ul, .layer ul, .window ul {
  list-style: none;
  margin: 10px 0 14px;
  padding-left: 0;
}
.card ul li, .layer ul li, .window ul li {
  position: relative;
  padding-left: 19px;
  margin: 0 0 9px;
  color: var(--body);
  line-height: 1.55;
}
.card ul li::before, .layer ul li::before, .window ul li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: .58em;
  width: 6px;
  height: 6px;
  border-radius: 1.5px;
  transform: rotate(45deg);
  background: color-mix(in srgb, var(--brass) 78%, var(--paper));
}
.card ol, .layer ol { padding-left: 22px; margin: 10px 0 14px; }
.card ol li, .layer ol li { margin: 0 0 9px; padding-left: 4px; line-height: 1.55; }
.card ol li::marker, .layer ol li::marker {
  color: var(--brass);
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12px;
}

/* rule-id stacks: quiet, tight, clearly meta rather than content */
.src {
  font-size: 10.5px;
  letter-spacing: .03em;
  margin-top: 8px;
  opacity: .92;
}
.src + .src { margin-top: 2px; }
.cite-line { margin-top: 16px; }

/* rule ids under a citation: a wrapping row of small badges instead of a
   stack of five one-line paragraphs -- same text, a third of the height */
.cite-hero .src:not(.open-cite) {
  display: inline-block;
  margin: 6px 6px 0 0;
  padding: 2px 8px;
  border: 1px solid color-mix(in srgb, var(--line) 85%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--sunk) 60%, transparent);
  font-size: 10px;
  letter-spacing: .05em;
}

/* --------------------------------------------------------------------- cards */
.card {
  border-radius: var(--pv-r-card);
  border-color: color-mix(in srgb, var(--line) 72%, transparent);
  box-shadow: var(--pv-shadow-2), var(--pv-lift);
  padding: 22px 20px;
  margin-bottom: 18px;
}
.card::before {
  height: 3px;
  border-radius: var(--pv-r-card) var(--pv-r-card) 0 0;
  background: linear-gradient(90deg, var(--brass) 0%,
    color-mix(in srgb, var(--brass) 45%, transparent) 45%, transparent 78%);
  opacity: .6;
}

/* read first, judge after: send the per-section feedback controls to the
   foot of the card, beside the challenge control they belong with.
   Flex children keep document order unless ordered, so only feedback moves. */
article.card { display: flex; flex-direction: column; }
article.card > * { min-width: 0; }
article.card > .tag { align-self: flex-start; }  /* don't stretch the pill */
article.card > .verdict { order: 97; margin-top: 18px; }
article.card > button[data-ch-open] { order: 98; }
article.card > .challenge { order: 99; }
article.card > .ch-msg { order: 100; }

.layer {
  border-radius: 12px;
  padding: 16px 14px;
  border-color: color-mix(in srgb, var(--line) 80%, transparent);
}
.layer.locked {
  border-style: dashed;
  background:
    repeating-linear-gradient(-45deg,
      color-mix(in srgb, var(--line) 26%, transparent) 0 1px,
      transparent 1px 9px),
    var(--sunk);
}
.layer.locked h2 { color: var(--body); }

/* --------------------------------------------------------- interactive states */
button {
  transition: background-color .16s ease, border-color .16s ease,
              color .16s ease, box-shadow .16s ease, transform .12s ease,
              opacity .16s ease;
}
:focus-visible {
  outline: 2px solid var(--deep);
  outline-offset: 2px;
  border-radius: 4px;
}
button:focus-visible { outline: 2px solid var(--deep); outline-offset: 2px; }

.primary {
  border-radius: var(--pv-r-ctl);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--deep) 90%, #fff) 0%, var(--deep) 62%);
  border-color: color-mix(in srgb, var(--deep) 85%, #000);
  color: var(--paper);           /* dark text on the light-blue dark-mode deep */
  font-weight: 600;
  letter-spacing: .01em;
  box-shadow: var(--pv-shadow-3);
}
@media (prefers-color-scheme: light) { .primary { color: #fff; } }
.primary:hover { filter: brightness(1.06); }
.primary:active { transform: translateY(1px); box-shadow: 0 1px 3px rgba(20,24,36,.3); }
.primary:disabled { filter: none; box-shadow: none; }

.ghost {
  border-radius: var(--pv-r-ctl);
  border-color: color-mix(in srgb, var(--line) 85%, var(--ink));
}
.ghost:hover { border-color: var(--deep); background: var(--deep-soft); }
.ghost:active { background: color-mix(in srgb, var(--deep-soft) 80%, var(--deep)); }

.chips button {
  border-radius: 999px;
  padding: 9px 16px;
  background: var(--card);
  box-shadow: var(--pv-shadow-1);
}
.chips button:hover { border-color: var(--deep); }
.chips button.on {
  border-color: var(--deep);
  background: var(--deep-soft);
  color: var(--deep);
  font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--deep), var(--pv-shadow-1);
}
.verdict .chips button.on {
  border-color: var(--ok); color: var(--ok);
  box-shadow: inset 0 0 0 1px var(--ok), var(--pv-shadow-1);
}

.answers button, .langs button, .hits { border-radius: 12px; }
.answers button { transition-duration: .16s; }
.answers button:hover { border-color: var(--deep); background: var(--deep-soft); }
.hits { box-shadow: var(--pv-shadow-2); }
.hits button:hover { background: var(--deep-soft); }
.langs button:hover { border-color: var(--deep); background: var(--deep-soft); }

input, select, textarea {
  border-radius: var(--pv-r-ctl);
  transition: border-color .16s ease, background-color .16s ease, box-shadow .16s ease;
}

.back { font-weight: 500; }
.back:hover { color: var(--ink); }

/* --------------------------------------------------------------- the verdict */
.verdict { padding-top: 12px; }
.verdict .chips button { min-height: 40px; padding: 7px 14px; font-size: 14px; }
.verdict .verdict-note { border-radius: var(--pv-r-ctl); }

/* ------------------------------------------------------------------- notices */
.warn, .okbox, .uncited, .note {
  border-radius: 10px;
}
.warn {
  border: 1px solid color-mix(in srgb, var(--warn) 38%, transparent);
  border-left: 3px solid var(--warn);
}
.okbox {
  border: 1px solid color-mix(in srgb, var(--ok) 38%, transparent);
  border-left: 3px solid var(--ok);
}
.uncited {
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  border-left: 3px solid var(--warn);
  border-radius: 10px;
}
.note {
  border-left: 2px solid color-mix(in srgb, var(--brass) 55%, var(--line));
  border-radius: 0;
}
.err { border-radius: 10px; box-shadow: var(--pv-shadow-1); }

/* ------------------------------------------------------------------ tags etc */
.tag {
  border-radius: 999px;
  padding: 3px 10px;
  background: color-mix(in srgb, var(--card) 55%, transparent);
}
.tag.astro     { background: color-mix(in srgb, var(--deep) 8%, transparent); }
.tag.vastu     { background: color-mix(in srgb, var(--brass) 9%, transparent); }
.tag.numerology{ background: color-mix(in srgb, var(--accent) 8%, transparent); }

.dots { margin: 2px 0 20px; }
.dots i { transition: background-color .3s ease, transform .3s ease; }
.dots i.on { transform: scale(1.35); }

/* ------------------------------------------------------------------- muhurta */
.window {
  border-radius: 12px;
  background: var(--card);
  border-left: 3px solid var(--brass);
  padding: 14px;
  box-shadow: var(--pv-shadow-1);
}
.window h3 { font-size: 12.5px; line-height: 1.5; }
.sense { display: flex; align-items: center; gap: 7px; }
.sense::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--faint); flex: none;
}
.sense.favourable::before { background: var(--ok); }
.sense.set_aside::before  { background: var(--warn); }
.sense.lifted::before     { background: var(--deep); }

/* -------------------------------------------------------------------- charts */
.chart-hold {
  border-radius: 12px;
  padding: 14px 10px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--sunk) 45%, var(--card)) 0%, var(--card) 30%);
  box-shadow: var(--pv-shadow-1), var(--pv-lift);
  border-color: color-mix(in srgb, var(--line) 80%, transparent);
}
.chart-card { margin-bottom: 20px; }
.chart-card .note { border-left: 0; padding-left: 0; font-size: 13px; }

.kundli .k-cell  { stroke: color-mix(in srgb, var(--line) 72%, var(--faint)); }
.kundli .k-line  { stroke: color-mix(in srgb, var(--line) 72%, var(--faint)); }
.kundli .k-body  { font-size: 11.5px; letter-spacing: .02em; }
.kundli .k-sign, .kundli .k-house { font-size: 9.5px; }
.kundli .k-asc   { font-size: 9.5px; }
.kundli .k-wash  { opacity: .05; }
.kundli .k-lagna { fill: color-mix(in srgb, var(--deep) 11%, var(--card)); }

/* ashtakavarga: keep the shade neutral (more / less, never good / bad)
   but let "more" carry a touch more pigment so the eye ranks cells */
.avgrid .av-hi { fill: color-mix(in srgb, var(--deep) 15%, var(--card)); }
.avgrid .av-lo { fill: color-mix(in srgb, var(--sunk) 80%, var(--paper)); }
.avgrid .av-n  { font-size: 21px; }

/* --------------------------------------------------------------------- loshu */
.loshu {
  gap: 8px;
  background: transparent;
  border: 0;
  margin: 12px auto 16px;
}
.loshu div {
  border: 1px solid var(--hair);
  border-radius: 12px;
  min-height: 74px;
  padding-top: 12px;
  box-shadow: var(--pv-shadow-1);
}
.loshu b { font-weight: 600; }

/* ------------------------------------------------------------------ timeline */
.bar {
  height: 22px;
  border-radius: 8px;
  border-color: color-mix(in srgb, var(--line) 80%, transparent);
  box-shadow: var(--pv-shadow-1);
}

/* ---------------------------------------------------------------- invocation */
.l365-inv-art { filter: drop-shadow(0 2px 6px color-mix(in srgb, var(--brass) 30%, transparent)); }
.l365-inv-open { margin-bottom: 24px; }

/* ------------------------------------------------------------------ footer */
.foot {
  background: color-mix(in srgb, var(--card) 84%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
  border-top: 1px solid color-mix(in srgb, var(--line) 80%, transparent);
  padding-bottom: env(safe-area-inset-bottom, 0);
  box-shadow: 0 -8px 24px rgba(20, 24, 36, .05);
}
@media (prefers-color-scheme: dark) {
  .foot { box-shadow: 0 -8px 24px rgba(0, 0, 0, .35); }
}
.foot button { font-weight: 500; letter-spacing: .02em; transition: color .16s ease; }

/* ----------------------------------------------------------- loading state
   The app renders exactly one bare <p> while a reading is computed.
   Make the wait feel intentional: the word, then a breathing skeleton of
   the reading that is coming. No spinner.                                   */
#root > p:only-child, main#root > p:only-child {
  margin: 18vh auto 0;
  max-width: 320px;
  text-align: center;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .19em;
  text-transform: uppercase;
  color: var(--faint);
}
#root > p:only-child::before {
  content: "";
  display: block;
  width: 44px; height: 44px;
  margin: 0 auto 18px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background:
    radial-gradient(circle at 50% 50%,
      color-mix(in srgb, var(--brass) 26%, transparent) 0%, transparent 62%);
}
#root > p:only-child::after {
  content: "";
  display: block;
  margin: 26px auto 0;
  height: 132px;
  border-radius: 12px;
  background:
    linear-gradient(var(--sunk) 0 0) 0 0    / 42% 14px,
    linear-gradient(var(--sunk) 0 0) 0 28px / 100% 10px,
    linear-gradient(var(--sunk) 0 0) 0 48px / 92% 10px,
    linear-gradient(var(--sunk) 0 0) 0 68px / 96% 10px,
    linear-gradient(var(--sunk) 0 0) 0 96px / 34% 12px,
    linear-gradient(var(--sunk) 0 0) 0 118px / 88% 10px;
  background-repeat: no-repeat;
}
@media (prefers-reduced-motion: no-preference) {
  #root > p:only-child          { animation: pv-breathe 2.4s ease-in-out infinite; }
  #root > p:only-child::before  { animation: pv-halo 2.4s ease-in-out infinite; }
  @keyframes pv-breathe { 0%,100% { opacity: .55; } 50% { opacity: 1; } }
  @keyframes pv-halo    { 0%,100% { transform: scale(.94); } 50% { transform: scale(1.04); } }
}

/* ------------------------------------------------------------- scroll reveal
   polish.js stamps .pv on reveal candidates and .pv-in when they enter the
   viewport. The pre-state exists ONLY under html.pv-motion, which the script
   sets ONLY when JS runs and the user has not asked for reduced motion --
   with no JS, or with reduced motion, nothing is ever hidden.               */
html.pv-motion .pv {
  opacity: 0;
  transform: translateY(14px);
}
html.pv-motion .pv-in {
  opacity: 1;
  transform: none;
  transition: opacity .55s cubic-bezier(.22, .61, .36, 1),
              transform .55s cubic-bezier(.22, .61, .36, 1);
  transition-delay: var(--pv-d, 0ms);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
  html.pv-motion .pv { opacity: 1; transform: none; }
  .dots i.on { transform: none; }
}

/* ------------------------------------------------------------------- page 1
   THE SHOWCASE. It shows a real chart and a real grid and asks for nothing,
   so it needs a wider column than the rest of the app: the reading measure is
   430px and a 320-wide chart beside a 218-wide grid does not fit in it.
   `draw()` stamps the current screen on <html>, so this applies to page 1
   alone and no other screen has to remember to undo it.
   Every colour below is an existing token -- --brass and --ok are already the
   logo's gold and green, so the brand needed no new palette.               */
html[data-screen="home"] .wrap { max-width: 780px; }

/* HIS BRAND, IN THE COMPACT FORM. The one asset the site serves is 1620x428 --
   nearly 4:1 -- which across a hero reads as a thin band ("wide spread which is
   not we want"). This is the same artwork restacked, emblem over wordmark, so
   it occupies a near-square block. width/height are on the tag to reserve the
   box and stop the page jumping as it decodes. Narrower than the Ganesha mark
   above it on purpose: the opening mark should lead.                        */
.show-logo {
  display: block;
  width: 100%;
  max-width: 150px;
  height: auto;
  margin: 0 auto 14px;
}
/* THREE MARKS NOW STACK ABOVE THE HEADLINE -- Ganesha, the emblem, the wordmark
   -- and at their natural sizes they pushed the chart below the fold, which
   defeats the point of a page whose job is to SHOW something. So the header is
   deliberately tight: Ganesha leads but small, and the brand block is smaller
   still. Both are marks, not the message.                                   */
html[data-screen="home"] .home-mark { margin: 0 auto 6px; }
html[data-screen="home"] .home-mark svg { width: 58px; height: 58px; }
html[data-screen="home"] .wrap { padding-top: 16px; }
/* IN DARK MODE IT GETS A PLATE, NOT A FILTER. The wordmark's "LIFE" and "365"
   are a deep green that all but vanishes on a #0C1017 page. Brightness filters
   were tried at 1.28 and 1.6 (out/_logo_dark.html, six treatments side by side)
   and both leave the green the weakest thing on the page while also shifting
   every hue in his logo. A plate changes only what sits BEHIND the artwork, so
   the brand keeps its exact colours -- and warm off-white rather than pure
   white, which reads as part of a dark page instead of a hole punched in it.
   The PNG is transparent, so the background belongs on the image itself.    */
@media (prefers-color-scheme: dark) {
  .show-logo {
    background: #FBF7EE;
    padding: 10px 16px;
    border-radius: 12px;
    max-width: 300px;
  }
}

/* A heading that is named but not shown, for when the logo has already said it.
   Not display:none -- that would take it out of the accessibility tree too.  */
.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

html[data-screen="home"] .home-h {
  margin: 0 auto 12px;
  max-width: 24ch;
  text-align: center;
  font-size: clamp(25px, 4.6vw, 34px);
  line-height: 1.18;
  letter-spacing: -.012em;
  text-wrap: balance;
}

.show-lede {
  margin: 0 auto 22px;
  max-width: 46ch;
  text-align: center;
  color: var(--body);
  font-size: 16.5px;
}

/* Where and when, stated before the figures rather than buried under them. */
.show-when {
  margin: 0 0 12px;
  text-align: center;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 12.5px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--brass);
}

.show-pair {
  display: grid;
  grid-template-columns: 1fr;
  gap: 18px;
  justify-items: center;
  margin: 0 0 18px;
}
@media (min-width: 720px) {
  .show-pair {
    grid-template-columns: 320px minmax(0, 1fr);
    align-items: start;
    gap: 26px;
  }
}
.show-fig { margin: 0; width: 100%; max-width: 340px; }
.show-fig .loshu { margin: 0 auto; }
.show-col { display: grid; gap: 18px; justify-items: center; width: 100%; }

/* THE DEVANAGARI GRAHA MARKS. Fraunces and IBM Plex have no Devanagari, so
   without this the glyphs fall to a system serif that sits at a different
   optical size to the Latin house numbers around them. A little larger than
   the Latin labels because Devanagari carries its weight above the baseline
   and reads small at the same nominal size.

   IBM Plex Sans sits between the two for a reason that only showed up under
   CSS.getPlatformFontsForNode: these marks are not all Devanagari. A
   retrograde graha is labelled "गु के R", and the R and the spaces fall
   outside the bundled face's unicode-range, so they were being painted by
   Nirmala UI -- a font that exists on Windows and on nothing a reader in
   India is likely to be holding.                                            */
.kundli-deva .k-body {
  font-family: "Noto Sans Devanagari", "IBM Plex Sans", "Nirmala UI",
               system-ui, sans-serif;
  font-size: 13px;
  font-weight: 600;
}

/* His name in numbers. Three figures, each with what it counts, so a reader is
   never left to guess which number means what.                              */
.show-name { text-align: center; }
.show-name-word { margin: 0 0 10px; }
.show-name-word b {
  display: block;
  /* Same reason as .k-body above: anything in the name that is not Devanagari
     should still come from a bundled face, not from whatever the OS has. */
  font-family: "Noto Sans Devanagari", "IBM Plex Sans", "Nirmala UI", serif;
  font-size: 30px;
  line-height: 1.1;
  font-weight: 600;
  color: var(--ink);
}
.show-name-word i {
  display: block;
  font-style: normal;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--faint);
}
.show-name-nums {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}
.show-name-nums div { background: var(--card); padding: 9px 4px; }
.show-name-nums span {
  display: block;
  font-size: 10.5px;
  line-height: 1.3;
  color: var(--faint);
}
.show-name-nums b {
  display: block;
  margin-top: 3px;
  font-family: Fraunces, Georgia, serif;
  font-size: 24px;
  line-height: 1;
  color: var(--brass);
}
.show-fig figcaption {
  margin-top: 10px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--faint);
}
.show-fig figcaption b { color: var(--ink); font-weight: 500; }

/* THE HONEST LINE, AND IT IS NOT AN APOLOGY. It is the sentence none of the
   incumbents would print, so it is set as a statement -- a quiet brass rule
   beside it -- rather than as a warning.                                    */
.show-not {
  margin: 0 auto 22px;
  max-width: 60ch;
  padding-left: 12px;
  border-left: 2px solid color-mix(in srgb, var(--brass) 55%, transparent);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--faint);
}

/* The three legs. These had NO styling at all -- three bare <h2>s under three
   motifs -- which is a good part of why the first screen read as unfinished. */
.home-legs {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin: 4px 0 14px;
}
.home-leg {
  padding: 14px 10px;
  text-align: center;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--pv-r-card, 14px);
  box-shadow: var(--pv-shadow-1);
}
.home-leg h2 {
  margin: 8px 0 0;
  font-size: 14.5px;
  line-height: 1.35;
  color: var(--ink);
}
.home-leg-art { color: var(--brass); }
.home-leg-art svg { width: 40px; height: 40px; display: block; margin: 0 auto; }
.show-legs {
  margin: 0 auto 20px;
  max-width: 52ch;
  text-align: center;
  font-size: 13.5px;
  color: var(--faint);
}
html[data-screen="home"] .primary { max-width: 340px; margin: 0 auto; display: block; }

/* ---------------------------------------------------------- language picker
   Three of the six packs are ~11% translated and were offered as first-class
   choices, so a reviewer picking Kannada got an English app with no warning.
   The founder's ruling was to keep all six visible and label the incomplete
   ones rather than hide them, which keeps the reach and stays honest.

   The percentage is deliberately quiet -- it is a fact about our progress, not
   a warning about their choice, and styling it as an alert would make picking
   your own language feel like a mistake. */
.langs button[data-partial] { position: relative; }
.lang-part {
  display: inline-block; margin-left: 6px; padding: 1px 5px;
  font-family: "IBM Plex Mono", monospace; font-size: 10px; line-height: 1.5;
  color: var(--faint); background: var(--sunk);
  border: 1px solid var(--hair); border-radius: 5px; vertical-align: 2px;
}
.lang-warn { max-width: 44ch; margin-top: 14px; }

/* LONG SECTIONS FOLD. Native <details>, so there is no handler to bind and it
   survives the re-render a verdict tap causes. The summary is styled as a quiet
   control rather than a button: opening it is optional, and the reading should
   not look like it is asking. */
details.fold { margin: 14px 0 16px; }
/* --ink-soft and the three rgba() browns below were the only hard-coded
   colours in this file, and they are the reason the fold was unreadable in
   dark: #6b6357 is a dark brown on a #0C1017 page. Tokens instead, so one
   declaration serves both themes -- which is the rule everywhere else here. */
details.fold > summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 14.5px;
  letter-spacing: 0.01em;
  /* 44px on a phone: this is a tap target, and it was 26px tall. */
  min-height: 44px;
  padding: 9px 0;
  color: var(--body);
  border-bottom: 1px solid color-mix(in srgb, var(--brass) 52%, transparent);
}
details.fold > summary::-webkit-details-marker { display: none; }
/* A drawn chevron that ROTATES rather than two different glyphs: the open and
   closed states then differ by a shape a reader can see at a glance, and the
   mark keeps its size when the type size changes under it. */
details.fold > summary::after {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform .18s ease;
}
details.fold[open] > summary::after {
  content: "";
  transform: rotate(45deg);
}
details.fold > summary:hover { color: var(--ink); }
details.fold > summary:focus-visible { outline: 2px solid var(--deep); outline-offset: 3px; }
/* Opened, the rest of the section has to be visibly PART OF the fold, or the
   reader cannot tell where the thing they opened ends. A recessed plate rather
   than a rule down the side: the section's caveat directly underneath already
   carries a brass side-rule, and two ruled blocks in a row read as one thing. */
details.fold > p {
  margin: 12px 0 0;
  padding: 14px 16px;
  border-left: 0;
  border-radius: 10px;
  background: color-mix(in srgb, var(--sunk) 60%, var(--card));
  color: var(--body);
  font-size: 16px;
  line-height: 1.72;
}

/* THE LIMIT, ONCE, AT THE FOOT OF THE READING. Quiet: it is candour, not a
   warning, and it should not be the loudest thing on the page.
   Set as a COLOPHON -- a short centred rule and one line under it -- rather
   than a full-bleed border, because a rule that runs the whole width reads as
   a section divider and this is the last word of the reading, not a new part.
   .card::before is already an absolutely-positioned brass bar on every card;
   made static here it becomes that short rule, so the ornament costs nothing
   and no other card is affected. */
.card.limit-once {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  margin-top: 30px;
  padding: 0;
  text-align: center;
}
.card.limit-once::before {
  position: static;
  display: block;
  width: 76px;
  height: 1px;
  margin: 0 auto 14px;
  border-radius: 0;
  background: color-mix(in srgb, var(--brass) 60%, transparent);
  opacity: 1;
}
/* opacity .78 on --faint fell to about 2.7:1 in light. --body at full opacity
   is both quieter-looking and comfortably legible in either theme. */
.card.limit-once p.note {
  margin: 0 auto;
  max-width: 36ch;
  opacity: 1;
  color: var(--body);
  font-size: 13px;
  border-left: 0;
  padding-left: 0;
}

/* ==========================================================================
   THE MIDDLE SCREENS
   ==========================================================================
   The landing page and the closing page had a pass; everything between them
   had not, and between them is where a reviewer spends every minute. The
   complaint this section answers is that the app CHANGES PERSONALITY as you
   walk it: `problem`, `l0` and `mode` are set pages with headings and prose,
   while `astroExact`, `astroRough` and `astroPlace` are three bare form rows
   with a shouted mono label where the heading should be, and the reading
   screens are a wall of white boxes with no rank inside them.

   Everything below is additive and every colour is a token or a color-mix() of
   tokens, so there is no second copy for dark. Where a rule exists above, this
   overrides it by specificity rather than by editing it.
   ========================================================================== */

/* ------------------------------------------------------- 1. the field itself
   Inputs sat on --sunk, which in dark is #0F141C against a #0C1017 page -- the
   field was very nearly invisible, and a reader cannot fill in a box they
   cannot find. Ground them toward --card in BOTH themes so a field always
   reads as a thing you write in, and give the focus ring a translucent --deep
   rather than --deep-soft: the flat token is #161E2E in dark and disappeared.
   48px minimum, because these are the app's largest tap targets on a phone. */
input, select, textarea {
  min-height: 48px;
  padding: 14px 14px;
  line-height: 1.55;
  color: var(--ink);
  background: color-mix(in srgb, var(--card) 76%, var(--sunk));
  border-color: color-mix(in srgb, var(--line) 88%, var(--faint));
  box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ink) 5%, transparent);
}
input:hover, select:hover, textarea:hover {
  border-color: color-mix(in srgb, var(--deep) 40%, var(--line));
}
input:focus, select:focus, textarea:focus {
  background: var(--card);
  border-color: var(--deep);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--deep) 26%, transparent),
              inset 0 1px 2px color-mix(in srgb, var(--ink) 4%, transparent);
}
::placeholder { color: var(--faint); opacity: 1; }
/* The "what's on your mind" box is the one place a reader writes a sentence
   rather than a value. Give it the leading of a page, not of a form field. */
textarea {
  min-height: 136px;
  padding: 15px 15px;
  line-height: 1.7;
}

/* Field labels were --faint at 10px, which is about 3.5:1 on --paper -- the
   single worst contrast in the app, on the text that tells you what to type.
   --body is still visibly quieter than the heading above it and passes in
   both themes. */
label {
  color: var(--body);
  font-size: 10.5px;
  letter-spacing: .12em;
  margin-bottom: 7px;
}

/* ------------------------------------- 2. the three screens that had no head
   `astroExact`, `astroRough` and `astroPlace` each consist of ONE <label> and
   one control. That label is a full sentence -- "Birth time -- unlocks the
   house-by-house reading" -- and app.css was setting it in 10px uppercase
   mono, so the only words on the screen arrived as a field caption and the
   page read as unfinished next to `problem` and `mode`, which have headings.
   Same markup, same words: set as the question it is. text-transform is reset
   because the string is already sentence case in i18n.js. */
html[data-screen="astroExact"] #root > label,
html[data-screen="astroRough"] #root > label,
html[data-screen="astroPlace"] #root > label {
  font-family: Fraunces, "Noto Sans Devanagari", "Noto Sans Kannada",
    "Noto Sans Telugu", "Noto Sans Tamil", "Noto Sans Malayalam",
    "Noto Sans Bengali", Georgia, serif;
  font-size: 21px;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -.005em;
  text-transform: none;
  color: var(--ink);
  max-width: 26ch;
  margin: 6px 0 14px;
}

/* The problem screen's label is the h1 VERBATIM ("What's on your mind?"), so
   it was printed twice, once as a headline and once in shouting mono directly
   under it. Hidden the accessible way, not display:none -- the textarea keeps
   its label in the accessibility tree, and the visible heading already says
   it. Scoped to this one screen so no other label is affected. */
html[data-screen="problem"] #root > label[for="complaint"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* ------------------------------------ 2b. the one-page intake (`birthAll`)
   This screen landed in app.js while this pass was being written: it replaces
   the numerology / astro / astroExact / astroRough / astroPlace chain with a
   single form. Its three hooks -- p.field-h, .seg and .seg-b -- are styled at
   the FOOT of this file, in "the birth page's time control", by whoever added
   the screen. Nothing about them is restated here: two definitions of one
   control in one stylesheet is how a control ends up looking like neither.
   The contrast and the selected-state cue are picked up after that block. */

/* The submit sits directly under the email field with no margin of its own. */
html[data-screen="birthAll"] #bgo { margin-top: 20px; }

/* ------------------------------------------------- 3. the back link, and rank
   --faint on --paper is ~3.5:1; --body reads as the same quiet grey and
   passes. The chevron gives the word an affordance without touching markup. */
.back {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 44px;
  color: var(--body);
}
.back::before {
  content: "\2039";
  font-size: 19px;
  line-height: 1;
  color: var(--faint);
}

/* Quiet text that was TOO quiet to read. --faint is 3.5:1 on --paper, which
   fails at any size and these are all 10-14px. The mix keeps the meta layer
   visibly subordinate to body copy while clearing 4.5:1 in both themes. */
.note { color: var(--body); }
.src, .cite-line, .verdict-h, .foot button, .progress,
.hits small, .lang-part {
  color: color-mix(in srgb, var(--faint) 55%, var(--body));
}
.progress { opacity: 1; }
.quiet-run { opacity: 1; color: var(--body); }

/* ------------------------------------------------------ 4. chips that CHOOSE
   The selected chip was carried by colour and weight alone -- and in dark
   --deep-soft (#161E2E) is barely distinguishable from --card (#141A24), so
   the only real signal left was the border hue. A reader who cannot separate
   navy from grey had no way to see what they had picked.

   So the mark is a SHAPE: an empty square when the chip is off, a tick when it
   is on. The slot is a fixed flex basis, so toggling never reflows the row.
   Colour and weight stay as they were -- three redundant signals, not one.

   Scoped by attribute to the chips that express a CHOICE: the topic chips on
   `problem`, the area chips on `l0` (both the server's and the app's own
   fallback set), and the right/partly/wrong control inside a reading. The
   chart North/South switch and the 1-5 rating are also .chips and must NOT
   grow a checkbox -- they are a segmented control and a scale. */
.chips button[data-chip],
.chips button[data-area],
.chips button[data-ownarea],
.chips button[data-verdict] {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 46px;
  padding: 9px 15px;
}
.chips button[data-chip]::before,
.chips button[data-area]::before,
.chips button[data-ownarea]::before,
.chips button[data-verdict]::before {
  content: "";
  flex: 0 0 13px;
  height: 13px;
  border: 1.5px solid color-mix(in srgb, var(--line) 45%, var(--faint));
  border-radius: 4px;
  background: color-mix(in srgb, var(--paper) 60%, var(--card));
  transition: border-color .16s ease, background-color .16s ease;
}
.chips button[data-chip].on::before,
.chips button[data-area].on::before,
.chips button[data-ownarea].on::before,
.chips button[data-verdict].on::before {
  height: 8px;
  border: 0;
  border-left: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  border-radius: 0;
  background: none;
  transform: rotate(-45deg) translate(1px, -1px);
}

/* ------------------------------------------ 5. the chart North / South switch
   Also .chips, but a segmented control: which one is active was shown by the
   same navy ring as a checkbox chip. A brass underline says "this is the view
   you are looking at" by position rather than by hue, and the double inset
   ring goes away. */
.chart-switch button { box-shadow: none; }
.chart-switch button.on {
  background: var(--sunk);
  border-color: color-mix(in srgb, var(--brass) 45%, var(--line));
  box-shadow: inset 0 -2px 0 var(--brass);
  color: var(--ink);
}

/* ------------------------------------------------------ 6. choosing a reading
   The mode screen offered three choices in two different shapes: the primary
   one centred with its title and its explanation both bold and white, the
   other two left-aligned with a bold first line. Nothing ranked, and the
   centred one was unreadable as a title-plus-subtitle at all.

   One shape for all three: the name in the display face on its own line, the
   cost underneath in the quiet voice. `br` is dropped because the title is a
   block now and the break would add an empty line -- the markup is unchanged,
   only its layout. */
.mode-pick {
  text-align: left;
  padding: 16px 18px;
  min-height: 68px;
  line-height: 1.45;
  font-size: 14.5px;
  font-weight: 400;
  letter-spacing: 0;
}
.mode-pick br { display: none; }
.mode-pick strong {
  display: block;
  margin-bottom: 4px;
  font-family: Fraunces, "Noto Sans Devanagari", "Noto Sans Kannada",
    "Noto Sans Telugu", "Noto Sans Tamil", "Noto Sans Malayalam",
    "Noto Sans Bengali", Georgia, serif;
  font-size: 18.5px;
  font-weight: 500;
  letter-spacing: -.008em;
}
.ghost.mode-pick { color: var(--body); }
.ghost.mode-pick strong { color: var(--ink); }
.mode-pick + .mode-pick { margin-top: 10px; }

/* ------------------------------------------------- 7. the other big controls
   .ghost and .answers were app.css defaults with a radius on top. Give them
   the same 48px, the same leading and the same hairline weight as everything
   else, so a row of choices reads as one family across every screen. */
.ghost {
  min-height: 48px;
  padding: 15px 16px;
  line-height: 1.45;
  color: var(--ink);
}
.answers { gap: 10px; margin-top: 12px; }
.answers button {
  min-height: 48px;
  padding: 14px 16px;
  line-height: 1.45;
  color: var(--ink);
  border-width: 1px;
  border-color: color-mix(in srgb, var(--line) 85%, var(--faint));
  border-radius: var(--pv-r-ctl);
  box-shadow: var(--pv-shadow-1);
}

/* A disabled primary was the deep fill at opacity .38 with white text on it,
   which is both unreadable and reads as broken rather than as not-yet. Say
   "not yet" instead: a sunk plate, a dashed edge, and legible --body text. */
.primary:disabled {
  opacity: 1;
  filter: none;
  box-shadow: none;
  background: var(--sunk);
  border: 1px dashed color-mix(in srgb, var(--line) 60%, var(--faint));
  color: var(--body);
}

/* ------------------------------------------------------- 8. the place picker
   The result list is the one place the app shows search results, and the
   subtitle under each place -- timezone, coordinates, the approximation note
   -- was 10px --faint mono. The APPROXIMATE tag sat inline in front of the
   place name and pushed it onto a second line; it belongs above it, where it
   labels the row rather than interrupting it. */
.hits { margin: -6px 0 16px; max-height: 264px; }
.hits button {
  min-height: 48px;
  padding: 13px 14px;
  line-height: 1.45;
  color: var(--ink);
}
.hits small {
  margin-top: 4px;
  font-size: 11px;
  line-height: 1.5;
  letter-spacing: .01em;
}
.hits .tag.approx {
  display: block;
  width: max-content;
  margin: 0 0 6px;
}
/* Continue is the real action and sat wedged between two escape hatches with
   8px above and below it. Air, so the eye finds it first. */
html[data-screen="astroPlace"] #readAstro { margin-top: 16px; }

/* ---------------------------------------------- 9. "what we heard" (l0)
   The open questions render as .warn -- three amber alert boxes, and then the
   same two questions again underneath as a second set of amber boxes. Amber
   twice over made a page of ERRORS out of a page that is politely asking two
   questions, and it is the loudest screen in the walk by a wide margin.

   Same boxes, read as questions: a warm card ground, a brass rule at the
   edge, ink-coloured text. Scoped to this screen only -- a .warn on
   `astroPlace` ("we could not find this exact town") is a real caution about
   the reading and keeps its amber. */
html[data-screen="l0"] #root > .warn {
  padding: 13px 15px;
  margin: 0 0 10px;
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--ink);
  background: color-mix(in srgb, var(--card) 88%, var(--brass));
  border: 1px solid color-mix(in srgb, var(--brass) 28%, transparent);
  border-left: 3px solid color-mix(in srgb, var(--brass) 70%, transparent);
  border-radius: 10px;
}
html[data-screen="l0"] #root > h2 { margin-top: 24px; }
html[data-screen="l0"] #root > .note { margin-top: 20px; }
html[data-screen="l0"] #root > .primary { margin-top: 18px; }

/* -------------------------------------------- 10. a reading, set as a chapter
   A section had a heading, a lead line, a body and up to four kinds of
   apparatus, all at similar weight in similar grey, stacked in a white box --
   so at a glance there was no way to tell the finding from the caveat from the
   citation. Three levels, distinguished three different ways:

     the TITLE   display face, with a brass rule under it
     the LEAD    17.5px, --ink, no rule
     the BODY    16px/1.72, --body

   and the apparatus below a hairline. */
.card { margin-bottom: 22px; }
article.card { padding: 24px 20px 22px; }
article.card + article.card { margin-top: 24px; }
article.card::before { opacity: .72; }

/* The bottom margin, not a `h2 + *` rule: article.card is a flex column and
   .verdict is ordered to the foot, so the element that FOLLOWS the heading in
   the markup is not the one that follows it on screen. A margin on the heading
   opens the same gap whatever lands under it. */
article.card > h2 {
  margin: 4px 0 15px;
  padding-bottom: 11px;
  border-bottom: 1px solid color-mix(in srgb, var(--brass) 32%, transparent);
}

article.card > p.lead {
  font-size: 17.5px;
  line-height: 1.52;
  color: var(--ink);
  margin-bottom: 16px;
}
/* :not([class]) is doing real work -- .note, .warn, .src, .cite-line and
   .verdict-h are all <p> children of a card too, and must keep their own
   sizes. The body paragraph is the only unclassed one. */
article.card > p:not([class]) {
  font-size: 16px;
  line-height: 1.72;
  color: var(--body);
}
/* THE SECOND .lead IN A CARD IS THE CAVEAT. app.js reuses the class for
   sec.caveat, so the qualification at the foot was set larger and darker than
   the finding it qualifies -- it outranked the body it sat under. Same words,
   set as an aside. */
article.card > p.lead ~ p.lead {
  font-size: 15px;
  line-height: 1.62;
  color: var(--body);
  padding-left: 13px;
  border-left: 2px solid color-mix(in srgb, var(--brass) 38%, transparent);
}

/* Read first, judge after -- polish.js sends .verdict to the foot of the card
   already. Give it the hairline that makes it apparatus rather than content. */
article.card > .verdict {
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px solid color-mix(in srgb, var(--brass) 26%, var(--hair));
}
.verdict-h { letter-spacing: .1em; }
/* Both measured under the floor before this pass: the chips at 40px, the note
   field at 42px. app.css sets `min-height: 0` on the note with a class pair, so
   the plain `input` rule above cannot reach it -- said again at that weight. */
.verdict .chips button { min-height: 44px; padding: 8px 14px; }
.verdict .verdict-note { min-height: 46px; padding: 12px 13px; }

/* "No accuracy figure exists yet" is printed on EVERY section, so a reading of
   twelve sections carried twelve filled amber boxes -- the loudest thing on
   the page, repeating one fact. .limit-once already states it once at the foot
   where it belongs. Kept, with the warn colour on the rule so the meaning does
   not change, but set as a footnote. A standalone .warn is untouched. */
article.card > p.warn {
  margin: 14px 0 0;
  padding: 1px 0 1px 13px;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--body);
  background: none;
  border: 0;
  border-left: 2px solid color-mix(in srgb, var(--warn) 55%, transparent);
  border-radius: 0;
}

/* The L1 layer is a bordered, shadowed box whose only content is a stack of
   bordered, shadowed cards -- a frame around a set of frames, which is what
   made a long reading read as nested boxes rather than as pages. Only L1 is
   unframed; the L0 "what we heard" block and the locked L2/L3 teasers are
   boxes with their own content and stay boxes. */
.layer[data-layer="L1"] {
  padding: 0;
  margin: 0;
  border: 0;
  background: none;
  box-shadow: none;
}
.layer[data-layer="L0"] { padding: 18px 16px; margin: 16px 0 20px; }
.layer[data-layer="L0"] > h2 { margin-bottom: 10px; }

/* ----------------------------------------------------- 11. the conversation
   app.css dims every reply the reader has already been given to opacity .72.
   In dark that puts --body body copy at roughly 3.9:1 -- the thread got
   HARDER TO READ the longer the conversation went, which is the opposite of
   what a transcript is for. The de-emphasis moves onto the text colour and the
   ground, where it costs no contrast, and the live turn gains a brass edge so
   "where you are" is a visible boundary rather than a slight opacity step. */
.thread { gap: 14px; }
.thread-ask {
  padding: 4px 0 4px 14px;
  margin: 0 0 22px;
  border-left-color: color-mix(in srgb, var(--brass) 80%, transparent);
}
.thread-ask p:last-child { font-size: 17px; line-height: 1.55; color: var(--ink); }
.t-us {
  padding: 16px 18px;
  border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  border-radius: 14px 14px 14px 4px;
  box-shadow: var(--pv-shadow-1);
}
.t-us:not(.t-live) {
  opacity: 1;
  color: var(--body);
  background: color-mix(in srgb, var(--card) 66%, var(--paper));
  box-shadow: none;
}
.t-us.t-live {
  border-color: color-mix(in srgb, var(--brass) 34%, var(--line));
  box-shadow: var(--pv-shadow-2);
}
.t-us.t-live > p.lead { color: var(--ink); }
.t-you {
  padding: 11px 15px;
  border: 1px solid color-mix(in srgb, var(--accent) 24%, transparent);
  border-radius: 14px 14px 4px 14px;
  color: var(--ink);
  font-size: 15px;
}
/* The citation disclosure inside a turn kept the browser's default triangle
   while the identical control in a reading (details.fold) is styled. Same
   control, same treatment, so the app does not change hand mid-sentence. */
.t-us > details > summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 9px 0;
  font-size: 14px;
  color: var(--body);
  border-bottom: 1px solid color-mix(in srgb, var(--brass) 45%, transparent);
}
.t-us > details > summary::-webkit-details-marker { display: none; }
.t-us > details > summary::after {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform .18s ease;
}
.t-us > details[open] > summary::after { transform: rotate(45deg); }
.t-us > details > summary:hover { color: var(--ink); }
.t-us > details > summary:focus-visible { outline: 2px solid var(--deep); outline-offset: 3px; }
.progress {
  margin: 24px 0 0;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
}

/* ------------------------------------------------- 12. one rhythm throughout
   The gap between a heading and what follows it was set per screen by whatever
   element happened to come next, so the same walk felt loose on one page and
   cramped on the next. One scale, applied to the direct children of #root on
   the screens between the landing page and the closing page. */
html[data-screen="problem"] #root > h1,
html[data-screen="l0"] #root > h1,
html[data-screen="mode"] #root > h1,
html[data-screen="astro"] #root > h1,
html[data-screen="birthAll"] #root > h1,
html[data-screen="numerology"] #root > h1,
html[data-screen="vastuDone"] #root > h1 {
  margin: 10px 0 14px;
  max-width: 28ch;
}
html[data-screen="birthAll"] #root > .lead { margin-bottom: 22px; }
html[data-screen="problem"] #root > .note,
html[data-screen="mode"] #root > .note,
html[data-screen="astro"] #root > .note,
html[data-screen="numerology"] #root > .note {
  margin: 12px 0 16px;
}
html[data-screen="problem"] #root > .chips { margin: 12px 0 20px; }
html[data-screen="l0"] #root > .chips { margin: 10px 0 18px; }
html[data-screen="problem"] #root > .primary,
html[data-screen="mode"] #root > .primary { margin-top: 4px; }
/* The dots are a position indicator, not decoration -- they had 2px above and
   20px below and read as attached to whatever came next. */
.dots { margin: 6px 0 22px; }

/* -------------------------------------------------------- 13. 390px, no bleed
   Nothing here overflows today, but three kinds of string in this app have no
   spaces in them and grow without warning: rule-id stacks (AST-AV-003...), the
   citation locators, and the coordinate line under a place. `break-word` only
   ever breaks a word that cannot fit, so ordinary prose is untouched. */
#root, #root p, #root h1, #root h2, #root h3, #root li, #root button,
#root summary, #root label { overflow-wrap: break-word; }
.src, .cite-line, .hits small, .window h3 { overflow-wrap: anywhere; }
/* A figure can never be wider than the column it is in, whatever its viewBox. */
#root svg, #root img, #root figure { max-width: 100%; }

/* --------------------------------------------------- 14. 44px, on every phone
   No blanket `min-height: 44px` rule here on purpose: a late one would have
   LOWERED the controls app.css already sets to 48. The floor is stated on each
   control where it is styled above -- 48px for inputs, .ghost, .answers and
   .hits, 46px for the choice chips, 44px for the verdict chips, the back link
   and both disclosure summaries -- and out/_pv_mid_shots.py measures every
   interactive element at 390px and fails the run if anything comes in short. */

/* The verdict control is single-choice, so its mark is a circle rather than the
   square used by the multi-select topic and area chips. The shape says how many
   answers are allowed before the reader has to find out by tapping. */
.chips button[data-verdict]::before { border-radius: 50%; }

/* The two controls the measurement found short, both from vastu_input.css --
   the facing toggle and the remove-a-room button, 40px each. A floor only:
   nothing else about them is touched, because that file has another writer. */
#viFaceTog, .vi-x { min-height: 44px; }

/* A note or a caution directly above the button that acts on it had no gap at
   all -- app.css gives .note and .warn a top margin and no bottom one, so the
   last line of the explanation sat against the top edge of Continue on
   `astroRough`, `astroPlace` and `vastuDone`. */
.note + .primary, .note + .ghost,
.warn + .primary, .warn + .ghost,
.okbox + .primary { margin-top: 16px; }

/* Dark is reached through prefers-color-scheme in app.css, but
   closing_panel.css switches on [data-theme="dark"] and app.js may follow.
   Everything above is written in tokens so it needs no dark twin; these two
   are the only places a shadow is stated in absolute black, and they are
   mirrored here so a data-theme switch does not leave them behind. */
[data-theme="dark"] {
  --pv-shadow-1: 0 1px 2px rgba(0, 0, 0, .5);
  --pv-shadow-2: 0 1px 1px rgba(0, 0, 0, .55), 0 10px 24px rgba(0, 0, 0, .40),
                 0 30px 60px -14px rgba(0, 0, 0, .55);
  --pv-shadow-3: 0 2px 6px rgba(0, 0, 0, .5), 0 12px 28px -6px rgba(0, 0, 0, .6);
  --pv-lift: inset 0 1px 0 rgba(255, 255, 255, .05);
}

/* ==========================================================================
   COLOUR, AND A FRONT PAGE THAT FITS ONE SCREEN
   Founder, 14 Sep, in quick succession: "UI is not colourful man", "also
   colourful charts above ..those are so dim", "every font placement is fucked
   up to be honest in home page", "also we must have all fit in a box no need
   of scroll".

   All four were true and measurable. The home page ran 1378px on an 844px
   phone; `.home-welcome` and both figcaptions computed to text-align:start
   while the heading, the legs and the button were centred, so one paragraph
   sat left against everything around it; and every stroke in the chart was
   --line (#D5D9E2), a near-white grey chosen for hairline rules in a table,
   which is why a chart printed on it looked switched off.

   THIS ADDS A PALETTE RATHER THAN RECOLOURING THE APP. The reading screens
   stay neutral on purpose -- a page of findings is not the place for
   decoration, and the two-audience scrub work depends on those tokens. What
   gets colour is the front page, which is first impression, and the CHARTS,
   which are the artefact people screenshot and send to each other.
   ========================================================================== */
:root {
  --saffron: #E8801A;
  --marigold: #F2A93B;
  --vermillion: #C8341F;
  --indigo: #2A3A7A;
  --peacock: #1B7A6E;
  --gold: #C9A24B;
  --cream: #FFFBF2;
}
@media (prefers-color-scheme: dark) {
  :root {
    --saffron: #F0983F;
    --marigold: #F6BE63;
    --vermillion: #E4614A;
    --indigo: #8FA6E8;
    --peacock: #4FBFAE;
    --gold: #D8B769;
    --cream: #171A22;
  }
}

/* ---- the chart, in colour ------------------------------------------------
   Every rule here overrides an earlier one that pointed at --line or --faint.
   The house grid is gold, the signs peacock, the planets indigo and heavier,
   a retrograde mark vermillion, and the ascendant house carries a saffron
   wash so the eye lands on it first -- which is where a reader should start. */
.kundli .k-bg { fill: var(--cream); }
.kundli .k-cell,
.kundli .k-line { stroke: var(--gold); stroke-width: 1.3; }
.kundli .k-sign { fill: var(--peacock); font-weight: 600; }
.kundli .k-house { fill: var(--gold); font-weight: 600; }
.kundli .k-body { fill: var(--indigo); font-weight: 600; }
.kundli .k-retro { fill: var(--vermillion); font-weight: 700; }
.kundli .k-asc { fill: var(--vermillion); font-weight: 700; }
.kundli .k-lagna { fill: color-mix(in srgb, var(--saffron) 13%, transparent); }
.kundli .k-lagna-mark,
.kundli .k-lagna-edge { stroke: var(--saffron); stroke-width: 2.2; }
.kundli .k-more { fill: var(--peacock); }
/* The wordmark washed across the middle of the chart was set at .05 opacity,
   which is invisible at small sizes and muddy at large ones. Slightly up, and
   in gold, so it reads as a watermark rather than a smudge. */
.kundli .k-wash { opacity: .07; fill: var(--gold); }

/* ---- the numbers grid, in colour ---------------------------------------- */
.loshu div {
  background: var(--cream);
  border: 1px solid color-mix(in srgb, var(--gold) 55%, transparent);
}
.loshu b { color: var(--vermillion); }

/* ---- one screen, no scrolling ------------------------------------------
   `100svh` rather than `100vh`: on a phone the dynamic viewport unit is the
   one that excludes the browser's own retracting chrome, which is exactly the
   space we are trying to fit inside. It still SCROLLS if a translation runs
   long or the text is zoomed -- clipping a Continue button to honour a layout
   wish would be building the dead end we keep having to remove. */
html[data-screen="home"] #root {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  background:
    radial-gradient(120% 70% at 50% 0%,
      color-mix(in srgb, var(--marigold) 15%, transparent) 0%,
      transparent 62%),
    radial-gradient(90% 55% at 50% 100%,
      color-mix(in srgb, var(--peacock) 10%, transparent) 0%,
      transparent 60%);
}
/* `width: 100%` here is what blew the logo up to the full column: this selector
   carries an ID, so it outranked `.show-logo { width: 104px }` further down and
   stretched an 813px image to 460px. Only the cap belongs on the generic rule --
   each child sizes itself. Every rule below is written through `#root` for the
   same reason, so specificity is uniform and order decides. */
html[data-screen="home"] #root > * { max-width: 460px; }

html[data-screen="home"] #root .home-head {
  margin: 0 auto 10px;
  padding: 6px 0 10px;
  border-bottom: 1px solid color-mix(in srgb, var(--gold) 34%, transparent);
  width: 100%;
}
html[data-screen="home"] #root .home-mark { margin: 0 auto; color: var(--saffron); }
html[data-screen="home"] #root .home-mark svg { width: 42px; height: 42px; }

html[data-screen="home"] #root .show-logo {
  width: 176px; height: auto; margin: 0 auto 10px; display: block;
}

/* THE ONE LINE THAT SHOULD BE BIG. Nothing on this page was: the brand h1 is
   visually hidden for screen-reader reasons, the welcome sat at body size, and
   the result was a page with no focal point at all. */
html[data-screen="home"] #root .home-quote {
  font-family: Fraunces, Georgia, serif;
  font-size: 25px;
  line-height: 1.24;
  font-weight: 500;
  color: var(--indigo);
  text-align: center;
  margin: 2px auto 10px;
  max-width: 24ch;
  text-wrap: balance;
}
html[data-screen="home"] #root .home-welcome {
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--body);
  text-align: center;      /* was `start`, alone among centred siblings */
  margin: 0 auto 16px;
  max-width: 44ch;
  text-wrap: pretty;
}

/* ---- the three legs, one colour each ----------------------------------- */
/* ---- three legs, and the real figure inside each -----------------------
   The card art is a SQUARE that each figure fills, so the chart, the nine
   directions and the number grid come out identically sized -- the founder's
   "left right both chart make same size ..looking so odd" applied to the row
   rather than to a separate pair. */
html[data-screen="home"] #root .home-legs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 0 auto 16px;
  width: 100%;
}
html[data-screen="home"] #root .home-leg {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 8px 6px 9px;
  border: 1px solid color-mix(in srgb, var(--gold) 42%, transparent);
  border-radius: 11px;
  background: var(--card);
  box-shadow: 0 1px 2px rgba(20, 24, 36, .05);
  min-width: 0;                 /* let the square shrink inside the column */
}
html[data-screen="home"] #root .home-leg-art {
  width: 100%;
  aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
/* The chart and the grid fill the square. `max-width: none` because `.kundli`
   is capped at 360px globally and `.loshu` uses fixed 72px columns -- both
   would otherwise refuse to shrink into a third of a phone. */
html[data-screen="home"] #root .home-leg-art .chart-hold,
html[data-screen="home"] #root .home-leg-art .kundli {
  width: 100%; height: auto; max-width: none; margin: 0;
}
html[data-screen="home"] #root .home-leg-art .chart-hold {
  border: 0; background: none; padding: 0;
}
html[data-screen="home"] #root .home-leg-art .loshu {
  grid-template-columns: repeat(3, 1fr);
  width: 100%; margin: 0; gap: 1px;
}
html[data-screen="home"] #root .home-leg-art .loshu div {
  min-height: 0; aspect-ratio: 1; padding: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
}
html[data-screen="home"] #root .home-leg-art .loshu b {
  font-size: 12px; margin: 0; line-height: 1;
}
html[data-screen="home"] #root .home-leg-art .loshu div > :first-child {
  font-size: 7px; line-height: 1;
}
/* Only a fallback motif is a bare svg child; the real figures are wrapped. */
html[data-screen="home"] #root .home-leg-art > svg { width: 46px; height: 46px; }

/* The nine directions, as the vastu picker lays them out. */
html[data-screen="home"] #root .dir9 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 1px;
  width: 100%; height: 100%;
}
html[data-screen="home"] #root .dir9 span {
  display: flex; align-items: center; justify-content: center;
  font-size: 8px; font-weight: 600; letter-spacing: .02em;
  color: var(--peacock);
  background: var(--cream);
  border: 1px solid color-mix(in srgb, var(--peacock) 30%, transparent);
}
html[data-screen="home"] #root .dir9 span.mid {
  background: color-mix(in srgb, var(--peacock) 16%, transparent);
}

html[data-screen="home"] #root .home-leg h2 {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .01em;
  margin: 0;
  line-height: 1.2;
  color: var(--ink);
}
html[data-screen="home"] #root .home-leg-astro { border-color: color-mix(in srgb, var(--saffron) 50%, transparent); }
html[data-screen="home"] #root .home-leg-astro .home-leg-art { color: var(--saffron); }
html[data-screen="home"] #root .home-leg-vastu { border-color: color-mix(in srgb, var(--peacock) 50%, transparent); }
html[data-screen="home"] #root .home-leg-vastu .home-leg-art { color: var(--peacock); }
html[data-screen="home"] #root .home-leg-num { border-color: color-mix(in srgb, var(--vermillion) 45%, transparent); }
html[data-screen="home"] #root .home-leg-num .home-leg-art { color: var(--vermillion); }

/* Smaller, to buy back the last of the scroll -- founder, 14 Sep: "just reduce
   size of continue as its small scroll as of now .. fit in one page." Held at
   44px minimum height, which is the smallest touch target that is still
   reliably tappable; below that we would be trading the tap for the layout. */
html[data-screen="home"] #root #homeGo {
  margin: 0 auto;
  max-width: 240px;
  min-height: 44px;
  padding: 11px 20px;
  font-size: 15px;
  border-radius: 9px;
  background: linear-gradient(180deg, var(--saffron), var(--vermillion));
  border-color: var(--vermillion);
  color: #fff;
}

/* Shorter phones: shrink the type and the mark, never hide the way in. */
@media (max-height: 700px) {
  html[data-screen="home"] #root .home-quote { font-size: 20px; }
  html[data-screen="home"] #root .show-logo { width: 138px; margin-bottom: 6px; }
  html[data-screen="home"] #root .home-mark svg { width: 34px; height: 34px; }
  html[data-screen="home"] #root .home-welcome { font-size: 13px; margin-bottom: 10px; }
}

/* A DESKTOP WINDOW IS SHORTER THAN A PHONE. With a taskbar and browser chrome
   the founder's own window leaves about 530px, where the page still ran 607px --
   the 77px of scroll he saw. The height is spent on the card squares: the row is
   capped at 460px wide, so each square was ~148px TALL. Capping the square is
   what buys the space back; the type only fine-tunes it. */
@media (max-height: 640px) {
  html[data-screen="home"] #root {
    padding-top: 6px;
    padding-bottom: calc(6px + env(safe-area-inset-bottom));
  }
  html[data-screen="home"] #root .home-leg-art { max-width: 88px; }
  html[data-screen="home"] #root .home-head { margin-bottom: 6px; padding: 2px 0 6px; }
  html[data-screen="home"] #root .home-mark svg { width: 28px; height: 28px; }
  html[data-screen="home"] #root .show-logo { width: 116px; margin-bottom: 4px; }
  html[data-screen="home"] #root .home-quote { font-size: 18px; margin-bottom: 6px; }
  html[data-screen="home"] #root .home-welcome {
    font-size: 12.5px; line-height: 1.42; margin-bottom: 10px;
  }
  html[data-screen="home"] #root .home-legs { margin-bottom: 10px; }
  html[data-screen="home"] #root .home-leg { gap: 5px; padding: 6px 5px 7px; }
  html[data-screen="home"] #root .home-leg h2 { font-size: 11.5px; }
  html[data-screen="home"] #root #homeGo {
    min-height: 40px; padding: 9px 18px; font-size: 14.5px;
  }
}

/* ---- the birth page's time control -------------------------------------
   Three choices that must read as one question, on the single intake page. */
.field-h {
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--ink);
  margin: 14px 0 6px;
}
.seg { display: flex; gap: 6px; margin: 0 0 10px; flex-wrap: wrap; }
.seg-b {
  flex: 1 1 30%;
  min-height: 44px;              /* a real touch target */
  padding: 8px 6px;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.25;
  color: var(--body);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 9px;
  cursor: pointer;
}
.seg-b.on {
  color: #fff;
  background: var(--indigo);
  border-color: var(--indigo);
  font-weight: 600;
}

/* -- two follow-ups to the block directly above, from the middle-screen pass --

   1. #fff on --indigo is right in light (about 10:1) and wrong in dark, where
      --indigo is the light periwinkle #8FA6E8: white on it measures about
      2.2:1, so the option the reader had CHOSEN was the least legible thing on
      the intake page. Screenshots: out/polish_mid/after2_dark_birthAll_*.png
      before this rule. --paper is the same move .primary already makes for the
      same token, so the two agree in both themes. Stated for prefers-color-
      scheme (which is how app.css switches) and for [data-theme], which
      closing_panel.css uses and app.js may adopt.

   2. Which option is selected was carried by fill colour alone. A brass rule
      under the chosen one says it again as a position, for anyone who cannot
      separate the indigo from the card grey -- and it is the same mark the
      chart's North/South switch uses, so "this is the one you are on" looks
      the same in both places. */
@media (prefers-color-scheme: dark) {
  .seg-b.on { color: var(--paper); }
}
[data-theme="dark"] .seg-b.on { color: var(--paper); }
.seg-b.on { box-shadow: inset 0 -3px 0 color-mix(in srgb, var(--brass) 85%, transparent); }

/* -- one-page intake: a field's footnote must not touch the next field's label -
   birthAll is now one long form, and several fields carry a p.note under them
   ("Not the legal name...", "We will use the middle of that band..."). A note
   is the END of the field above it; the label under it is the START of the next
   one. With no space between the two the reader sees one run-on block and the
   form loses the one-question-at-a-time feel the rest of the flow has -- see
   out/polish_mid/after3_light_birthAll_rough.png, where the band caveat and
   "BIRTH PLACE" sit on consecutive lines. The gap is larger than the label's
   own bottom margin so the label stays visibly bound to ITS input. */
html[data-screen="birthAll"] #root .note + label,
html[data-screen="birthAll"] #root .note + .field-h,
html[data-screen="birthAll"] #root .warn + label { margin-top: 22px; }
