:root {
    /* Native controls — the date picker's calendar popup, the time picker's
       list, scrollbars, form field internals — are drawn by the browser and
       take no CSS from us. `color-scheme` is the one lever that theme them,
       and without it Chrome opens a white calendar over the dark page. It is
       redeclared in each of the three ways dark can be reached, exactly like
       the palette. */
    color-scheme:light;
    --cream:#FFFAF3;
    --surface:#FFFFFF;
    --surface-2:#FAF4E9;
    --rail:#F6EFE2;
    --ink:#17120C;
    --ink-2:#4A3F31;
    --ink-3:#756853;
    --line:#E8DCC8;
    --line-soft:#F0E7D6;
    --gold:#C6943E;
    --gold-dk:#92600A;
    --gold-lt:#E8C98A;
    --gold-bg:#FBF3E3;
    --good:#2F7A57;
    --mid:#9E5A18;
    --bad:#B3261E;
    --good-bg:#EAF3ED;
    --mid-bg:#FBF0E4;
    --bad-bg:#FAEBEA;
    --pill-ink:#231C13;
    --pill-text:#FFFAF3;
    --gold-solid:#8A5A0B;
    --gold-solid-text:#FFF9F0;
    --sk:#E4D8C2;
    --sk-hi:#F7F1E6;
    --good-solid:#2F7A57;
    --good-solid-text:#FFFFFF;
    --ph:#7C6E5A;
    --cat-1:#6B4708;
    --cat-2:#D47142;
    --cat-3:#262C3A;
    --cat-4:#24756F;
    --font-serif:'Iowan Old Style','Palatino Linotype',Palatino,'Book Antiqua',Georgia,serif;
    --font-sans:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;
    --font-mono:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;
    --font-glyph:'Apple Symbols','Segoe UI Symbol','Noto Sans Symbols 2',serif;
    --text-xs:11px;
    --text-sm:12.5px;
    --text-base:14px;
    --text-md:15px;
    --text-lg:17px;
    --text-xl:21px;
    --text-2xl:27px;
    --text-3xl:34px;
    --text-4xl:46px;
    --space-1:4px;
    --space-2:8px;
    --space-3:12px;
    --space-4:16px;
    --space-5:22px;
    --space-6:30px;
    --space-7:44px;
    --space-8:64px;
    --r-sm:6px;
    --r-in:11px;
    --r-card:16px;
    --r-pill:999px;
    /* ── HOW WIDE THE PAGE GETS ──
       One number, because five rules used to carry it independently: the
       content wrap, the sticky nav's row, the hero's row, the footer's row
       and the app's main. They have to move together or the nav stops
       lining up with the thing under it — which is exactly the kind of
       thing nobody notices until every page is 180px out.
       1360, up from 1180. On a 1900px screen the old cap left 360px of
       nothing down each side. Text does not get wider with it: .l-sub and
       the copy blocks carry their own ch-based caps, so this widens the
       grids and the images, which is where the room was wanted. */
    --wrap:1360px;
    --tap-min:44px;
}

/* ── LIGHT IS THE DEFAULT, AND DARK IS A CHOICE ─────────────────────
   The OS media query used to be here, so a phone in dark mode got the
   dark palette without asking. That is the usual behaviour and it is not
   what this site wants: the brand is gold on cream, and a link shared
   from Instagram opened dark for most of the people who tapped it — the
   first impression of the product was a palette nobody chose.
   Dark is still fully built and still reachable: the toggle sets
   data-theme and the head script remembers it. What is gone is dark
   arriving unasked. */
:root[data-theme="dark"] {
    color-scheme:dark;
    --cream:#17120C;
    --surface:#1F1810;
    --surface-2:#241C13;
    --rail:#1B150E;
    --ink:#F6EFE3;
    --ink-2:#CFC2AC;
    --ink-3:#9A8B75;
    --line:#332A1E;
    --line-soft:#2A2217;
    --gold:#D9A94F;
    --gold-dk:#E8C98A;
    --gold-lt:#8A6A2E;
    --gold-bg:#2A2114;
    --good:#4EA87A;
    --mid:#D98E43;
    --bad:#D4635A;
    --good-bg:#1B2620;
    --mid-bg:#2A2115;
    --bad-bg:#2A1A18;
    --pill-ink:#F6EFE3;
    --pill-text:#17120C;
    --gold-solid:#D9A94F;
    --gold-solid-text:#17120C;
    --sk:#3A2F1E;
    --sk-hi:#54452C;
    --good-solid:#4EA87A;
    --good-solid-text:#17120C;
    --ph:#93846D;
    --cat-1:#E5B865;
    --cat-2:#AE5638;
    --cat-3:#D5DCEC;
    --cat-4:#4FA69E;
}
:root[data-theme="light"] {
    color-scheme:light;
    --cream:#FFFAF3;
    --surface:#FFFFFF;
    --surface-2:#FAF4E9;
    --rail:#F6EFE2;
    --ink:#17120C;
    --ink-2:#4A3F31;
    --ink-3:#756853;
    --line:#E8DCC8;
    --line-soft:#F0E7D6;
    --gold:#C6943E;
    --gold-dk:#92600A;
    --gold-lt:#E8C98A;
    --gold-bg:#FBF3E3;
    --good:#2F7A57;
    --mid:#9E5A18;
    --bad:#B3261E;
    --good-bg:#EAF3ED;
    --mid-bg:#FBF0E4;
    --bad-bg:#FAEBEA;
    --pill-ink:#231C13;
    --pill-text:#FFFAF3;
    --gold-solid:#8A5A0B;
    --gold-solid-text:#FFF9F0;
    --sk:#E4D8C2;
    --sk-hi:#F7F1E6;
    --good-solid:#2F7A57;
    --good-solid-text:#FFFFFF;
    --ph:#7C6E5A;
    --cat-1:#6B4708;
    --cat-2:#D47142;
    --cat-3:#262C3A;
    --cat-4:#24756F;
}

/* Font smoothing is a DARK-MODE-ONLY tool.
   -webkit-font-smoothing:antialiased switches macOS from subpixel to grayscale
   antialiasing, which renders type thinner and lighter. Light text on a dark
   ground needs that, otherwise it blooms. Dark text on a light ground does not:
   it comes out washed and under-weight, worst on large heavy type — which is
   why the hero headline, the card titles and the picker values looked faded in
   light mode while the body copy beside them looked fine. It was set globally
   on <body> in two stylesheets. Here it is scoped to the theme that wants it,
   in all three of the ways dark can be reached. */
:root[data-theme="dark"] body,
#th-dark:checked ~ .l-app, #th-dark:checked ~ .app {
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
}
:root[data-theme="light"] body {
  -webkit-font-smoothing:auto; -moz-osx-font-smoothing:auto;
}

/* Every placeholder in the product, in one place. Left unstyled, a browser
   picks its own — Chrome uses #757575 in BOTH themes, Safari and Firefox each
   use something else again, and Firefox additionally halves the opacity. So
   opacity is pinned as well as colour. */
::placeholder { color:var(--ph); opacity:1; }
::-moz-placeholder { color:var(--ph); opacity:1; }

/* A <select> that has not been answered yet is the same idea wearing a
   different element: "Choose a city" is a prompt, not a value. It cannot be
   reached with ::placeholder, so the option carries the state instead. */
select:has(option[value=""]:checked) { color:var(--ph); }
option { color:var(--ink); }
option[value=""] { color:var(--ph); }


/* ── text ─────────────────────────────────────────────── */
.c-label { font-family:var(--font-mono); font-size:10px; letter-spacing:.12em;
           text-transform:uppercase; color:var(--ink-3); }
/* The same eyebrow, inline rather than block, and the caption strip under a
   picker. Both were declared in horoscope.py, which is a page and not a
   stylesheet — so the Chinese page and the Lo Shu page, which use them and do
   not include that page's CSS, were rendering every label as 16px body text.
   Anything more than one page uses belongs here. */
.h-f-l { font-family:var(--font-mono); font-size:9.5px; letter-spacing:.1em;
         text-transform:uppercase; color:var(--ink-3); }
.h-f-hint { display:flex; flex-wrap:wrap; gap:6px 14px; align-items:baseline;
            font-family:var(--font-mono); font-size:10px; letter-spacing:.09em;
            text-transform:uppercase; color:var(--ink-3); margin:2px 0 18px; }
.c-note { font-size:var(--text-sm); line-height:1.55; color:var(--ink-3);
          margin-top:var(--space-3); padding-top:var(--space-3);
          border-top:1px solid var(--line-soft); }
.c-h1 { font-size:var(--text-3xl); line-height:1.1; letter-spacing:-.02em;
        font-weight:750; margin:0 0 var(--space-3); text-wrap:balance; }
.c-h2 { font-size:var(--text-xl); line-height:1.2; letter-spacing:-.015em;
        font-weight:750; margin:0 0 var(--space-2); text-wrap:balance; }
.c-h3 { font-size:var(--text-lg); font-weight:700; margin:0 0 var(--space-1); }
.c-serif { font-family:var(--font-serif); font-weight:600; }
.c-glyph { font-family:var(--font-glyph); font-variant-emoji:text;
           color:var(--gold-dk); }

/* ── tone modifiers, one place ────────────────────────── */
.c-t-good { color:var(--good); } .c-t-mid { color:var(--mid); }
.c-t-bad { color:var(--bad); }   .c-t-gold { color:var(--gold-dk); }

/* ── controls ─────────────────────────────────────────── */
/* text-decoration:none, because button(href=...) returns an <a> and an <a>
   underlines by default. Eight of these ship across the site and the one on
   the home page is "Talk to an astrologer" — the entire business, rendering
   as an underlined link inside a filled gold box. Two of the four buttons in
   the top bar looked right only because .l-signin and .l-signup happen to set
   it themselves, which is why nothing looked obviously broken. */
/* AN ICON AND A LABEL SIT SIDE BY SIDE. `inline-grid` with two children makes
   two ROWS, so every button given an icon stacked it above its own text and
   read as two things in a box. Two call sites had already worked around it
   privately — .c-as-b and .h-alt both re-declare display:inline-flex purely
   for this — and the third one was being written when it turned up. Nothing in
   the built site puts two non-icon children in a .c-btn, so `auto-flow:column`
   changes only the broken case. */
.c-btn { display:inline-grid; grid-auto-flow:column; gap:8px;
         place-items:center; min-height:44px;
         padding:0 18px; border-radius:10px; font-size:var(--text-base);
         font-weight:700; cursor:pointer; border:1px solid var(--line);
         background:var(--surface); color:var(--ink); text-align:center;
         text-decoration:none; }
/* AND `hidden` HAS TO WIN AGAINST BOTH OF THOSE DISPLAYS.
   `[hidden] { display:none }` is a USER-AGENT rule, and ANY author rule that
   sets display outranks the UA — so every .c-btn written with a `hidden`
   attribute rendered regardless. /onboarding/ shipped its "Try again" button
   on screen at all times because of it, which looks exactly like a button
   somebody meant to put there.
   On the component, so no page has to remember: .c-btn-full below sets
   display too and the same selector covers it. */
.c-btn[hidden] { display:none; }
.c-btn-primary { background:var(--gold-solid); border-color:var(--gold-solid);
                  color:var(--gold-solid-text); }
.c-btn-danger { color:var(--bad); }
.c-btn-sm { min-height:34px; padding:0 13px; font-size:var(--text-sm); }
.c-btn-lg { min-height:50px; padding:0 26px; font-size:var(--text-md); }
.c-btn-full { display:grid; width:100%; }
/* ── 34px IS A MOUSE HEIGHT, AND THIS IS THE COMPONENT, NOT A PAGE ──
   .c-btn is 44 and .c-btn-lg is 50; the small one is the only button on this
   site a thumb cannot reliably hit, and it is small because on a desktop it
   should be — a secondary action sitting beside a primary one.
   Found on "Show more reviews", which is the only route to the sixth review
   an astrologer has, on twenty-four pages. It was never a bug about reviews:
   EVERY .c-btn-sm on the site was 34px on a phone, and fixing it where it
   happened to be noticed would have left all the others. pointer:coarse, so
   the desktop keeps the height that was chosen for it. */
@media (pointer:coarse) { .c-btn-sm { min-height:var(--tap-min); } }

.c-chip { display:inline-block; font-size:var(--text-sm); font-weight:600;
          padding:6px 12px; border-radius:var(--r-pill); color:var(--ink-2);
          border:1px solid var(--line); background:var(--surface); }
.c-chip.c-on { color:var(--gold-dk); background:var(--gold-bg);
               border-color:var(--gold); }

.c-badge { display:inline-block; font-family:var(--font-mono); font-size:9.5px;
           letter-spacing:.09em; text-transform:uppercase; padding:4px 9px;
           border-radius:var(--r-sm); color:var(--ink-3);
           background:var(--surface-2); }
.c-badge.c-t-good { color:var(--good); background:var(--good-bg); }
.c-badge.c-t-mid  { color:var(--mid);  background:var(--mid-bg); }
.c-badge.c-t-bad  { color:var(--bad);  background:var(--bad-bg); }
.c-badge.c-t-gold { color:var(--gold-dk); background:var(--gold-bg); }

.c-field { margin-bottom:var(--space-3); }
.c-field label { display:block; font-family:var(--font-mono); font-size:10px;
                 letter-spacing:.1em; text-transform:uppercase;
                 color:var(--ink-3); margin-bottom:5px; }
/* radio_group(legend=…). A fieldset is the only element that can carry a name
   for a group of radios, and it arrives with a border, padding, and a
   min-content floor that refuses to shrink inside a grid column. All three
   off; the legend then dresses as the field labels above it, because to a
   reader it is one. */
.c-optf { border:0; padding:0; margin:0 0 var(--space-3); min-width:0; }
.c-optf > legend { display:block; padding:0; font-family:var(--font-mono);
                   font-size:10px; letter-spacing:.1em; text-transform:uppercase;
                   color:var(--ink-3); margin-bottom:5px; }
.c-field input { width:100%; min-height:46px; padding:0 13px;
                 font-size:var(--text-md); font-family:inherit; color:var(--ink);
                 background:var(--surface); border:1px solid var(--line);
                 border-radius:10px; }
.c-field input:focus { outline:2px solid var(--gold); outline-offset:1px; }
/* ── 16px ON A TOUCH SCREEN, OR iOS ZOOMS THE PAGE ──────────────────────
   Safari zooms into any input whose text is under 16px the moment it takes
   focus. Every text control on this site was under it — 15px on the form
   fields, 14px in the date picker, 12.5px on the coupon and email — so every
   tap on every field scaled the page up and left the reader to pinch back.

   AND THAT IS THE "WEIRD BAR". Once zoomed, the layout is wider than the
   visual viewport, so the document scrolls sideways and iOS draws its
   horizontal scroll indicator: a grey capsule, sitting at the bottom edge of
   the visual viewport, which with the keyboard up is the middle of the
   screen. Nothing on the page drew it. It is the scrollbar of a page that
   should never have been zoomed, and no amount of looking at our own CSS for
   a stray border would have found it.

   pointer:coarse, so the 15px stays on a desktop where it is right and where
   no zoom happens. Radios and checkboxes are excluded — they carry no text,
   several are sized deliberately, and Safari does not zoom for them. */
@media (pointer:coarse) {
  input:not([type=radio]):not([type=checkbox]):not([type=range]),
  textarea, select { font-size:16px; }
}
/* field(adorn=…): a control inside the input's box. The input keeps 44px clear
   on the right so text cannot run under it. */
.c-field-a { position:relative; display:block; }
.c-field-a input { padding-right:44px; }
.c-field-a > button { position:absolute; right:1px; top:1px; bottom:1px;
                 width:42px; display:flex; align-items:center;
                 justify-content:center; padding:0; border:0; cursor:pointer;
                 background:none; color:var(--ink-3);
                 border-radius:0 9px 9px 0; }
.c-field-a > button:hover { color:var(--ink); }
.c-field-a > button svg { width:19px; height:19px; display:block; }

.c-toggle { display:flex; align-items:flex-start; gap:var(--space-3);
            padding:11px 0; min-height:var(--tap-min); cursor:pointer;
            border-top:1px solid var(--line-soft); }
.c-toggle:first-child { border-top:0; }
.c-tog-b { flex:1; min-width:0; }
/* Off-screen rather than display:none — a hidden input is not focusable, and
   the focus ring below is the only thing telling a keyboard where it is. */
.c-sw-i { position:absolute; opacity:0; width:1px; height:1px; }
.c-sw { flex:none; width:38px; height:22px; border-radius:var(--r-pill);
        margin-top:1px; background:var(--line); position:relative;
        transition:background .15s; }
.c-sw::after { content:''; position:absolute; top:3px; left:3px; width:16px;
               height:16px; border-radius:50%; background:var(--surface);
               transition:left .15s; }
.c-sw-i:checked ~ .c-sw { background:var(--gold-solid); }
.c-sw-i:checked ~ .c-sw::after { left:19px; background:var(--gold-solid-text); }
.c-sw-i:focus-visible ~ .c-sw { outline:2px solid var(--gold); outline-offset:3px; }
@media (prefers-reduced-motion:reduce) {
  .c-sw, .c-sw::after { transition:none; }
}
.c-toggle b { display:block; font-size:var(--text-base); font-weight:700; }
.c-toggle i { display:block; font-style:normal; font-size:var(--text-sm);
              color:var(--ink-3); margin-top:1px; }

.c-acc { border:1px solid var(--line); border-radius:var(--r-in);
         background:var(--surface); padding:0 16px; margin-bottom:8px; }
.c-acc summary { list-style:none; cursor:pointer; padding:15px 0;
                 font-size:var(--text-md); font-weight:650; display:flex;
                 gap:11px; align-items:baseline; min-height:44px; }
.c-acc summary::-webkit-details-marker { display:none; }
.c-acc summary::before { content:'+'; font-family:var(--font-mono);
                         color:var(--gold-dk); flex:none; width:12px; }
.c-acc[open] summary::before { content:'\2212'; }
/* An accordion answer had never contained a link, so nothing had given one
   a colour — the first FAQ answer to link out came out in the browser's
   own -webkit-link. Set on the component, not on the page that noticed. */
.c-acc p a { color:var(--gold-dk); text-underline-offset:2px; }
.c-acc p { font-size:var(--text-base); line-height:1.65; color:var(--ink-2);
           margin:0; padding:0 0 15px 23px; }

.c-radio { position:absolute; left:-9999px; }
.c-sel { display:block; padding:15px 16px; cursor:pointer;
         border:1px solid var(--line); border-radius:var(--r-in);
         background:var(--surface); }
.c-sel:hover { border-color:var(--gold-lt); }

/* ── containers ───────────────────────────────────────── */
.c-card { background:var(--surface); border:1px solid var(--line);
          border-radius:var(--r-card); padding:20px; }
@media (min-width:760px) { .c-card { padding:24px 26px; } }
.c-card-title { font-size:var(--text-lg); font-weight:700; margin:4px 0 6px; }
.c-card a { color:var(--gold-dk); text-underline-offset:2px; }

.c-stack { display:grid; }
.c-gap-2 { gap:var(--space-2); } .c-gap-3 { gap:var(--space-3); }
.c-gap-4 { gap:var(--space-4); }
.c-cols-1 { grid-template-columns:1fr; }
.c-cols-2, .c-cols-3, .c-cols-4 { grid-template-columns:1fr; }
@media (min-width:640px) {
  .c-cols-2, .c-cols-3 { grid-template-columns:repeat(2,1fr); }
  .c-cols-4 { grid-template-columns:repeat(2,1fr); }
}
@media (min-width:900px) {
  .c-cols-3 { grid-template-columns:repeat(3,1fr); }
  .c-cols-4 { grid-template-columns:repeat(4,1fr); }
}

.c-band { background:var(--pill-ink); color:var(--pill-text);
          border-radius:var(--r-card); padding:26px 22px; display:grid;
          gap:var(--space-4); }
@media (min-width:760px) { .c-band { grid-template-columns:1.4fr 1fr;
                                       align-items:center; } }
.c-band h2 { margin:0 0 var(--space-2); font-size:var(--text-xl);
             font-weight:750; letter-spacing:-.015em; }
.c-band p { margin:0; opacity:.82; }
.c-band-a { display:grid; gap:var(--space-2); }

.c-sh { display:flex; flex-wrap:wrap; align-items:baseline; gap:10px;
        margin:var(--space-6) 0 var(--space-3); }
/* The section's own control, pushed to the far end of the heading row.
   flex-wrap above is what keeps it from crushing the title on a phone: it
   drops to its own line instead. Nothing else in a section head has ever
   overflowed, so the wrap is inert everywhere it is not needed. */
.c-sh-x { margin-left:auto; align-self:center; }
.c-sh-c { justify-content:center; text-align:center; }
.c-sh-n { font-family:var(--font-mono); font-size:var(--text-sm);
          color:var(--gold-dk); }
.c-sh-t { font-family:var(--font-mono); font-size:var(--text-sm);
          letter-spacing:.13em; text-transform:uppercase; font-weight:700; }
.c-sh-a { font-family:var(--font-serif); font-style:italic;
          font-size:var(--text-base); color:var(--ink-3); }
/* ON A PHONE THE ASIDE DROPS, NOT THE ACTION.
   flex-wrap above lets the row break, and at 390px the thing that broke was
   always the control: "See all 24" landed on a line of its own, right-aligned
   against nothing, reading as a stray button rather than as this section's way
   out. The italic aside is decoration and the control is a destination, so the
   aside is the one that belongs on the second line. */
@media (max-width:640px) {
  /* var(--space-6) above a heading is right under a wide band and is most of
     a thumb-swipe on a phone. The bands are already separated by their own
     padding and by .l-alt's background change. */
  .c-sh { row-gap:4px; margin-top:var(--space-4); }
  .c-sh-x { order:2; }
  .c-sh-a { order:3; flex:1 0 100%; }
}

/* ── data ─────────────────────────────────────────────── */
.c-table { width:100%; border-collapse:collapse; font-size:var(--text-base); }
.c-table th { font-family:var(--font-mono); font-size:9.5px; letter-spacing:.1em;
              text-transform:uppercase; color:var(--ink-3); font-weight:400;
              text-align:left; padding:0 12px 8px 0;
              border-bottom:1px solid var(--line); }
.c-table td { padding:10px 12px 10px 0; border-bottom:1px solid var(--line-soft);
              color:var(--ink-2); }
.c-table td:first-child { color:var(--ink); font-weight:650; }
/* A link in a cell is still a link, and nothing here painted one. The td sets
   a colour, the <a> inside it overrides with the UA default and the four hub
   tables shipped 1996 blue next to gold prose links — #0000EE in light, and
   Chrome's adapted #9E9EFF in dark. Same treatment as .l-sub a and .c-prose a,
   which is the site's one link colour. */
.c-table a { color:var(--gold-dk); text-underline-offset:2px; }
/* Reflow rather than scroll. A six-column table on a 390px phone clipped
   'Retrograd[e]' and nobody knew it scrolled. */
@media (max-width:700px) {
  .c-reflow, .c-reflow tbody, .c-reflow tr, .c-reflow td { display:block;
    width:100%; }
  .c-reflow thead { display:none; }
  .c-reflow tr { padding:12px 0; border-bottom:1px solid var(--line-soft); }
  .c-reflow tr:last-child { border-bottom:0; }
  .c-reflow td { border:0; padding:2px 0; display:flex;
                 justify-content:space-between; gap:14px; }
  .c-reflow td::before { content:attr(data-l); font-family:var(--font-mono);
    font-size:9.5px; letter-spacing:.09em; text-transform:uppercase;
    color:var(--ink-3); flex:none; padding-top:3px; }
  .c-reflow td:first-child { font-size:var(--text-lg); font-weight:750;
    padding-bottom:6px; }
  .c-reflow td:first-child::before { content:none; }
}

.c-kv-r { display:flex; justify-content:space-between; gap:16px;
          padding:9px 0; border-top:1px solid var(--line-soft);
          font-size:var(--text-base); }
.c-kv-r:first-child { border-top:0; }
.c-kv-r span { color:var(--ink-3); }
.c-kv-r b { text-align:right; }
.c-kv-r a { color:var(--gold-dk); text-underline-offset:2px; }

.c-stat { padding:14px 15px; border-radius:var(--r-in);
          background:var(--surface-2); }
.c-stat span { font-family:var(--font-mono); font-size:9.5px;
               letter-spacing:.11em; text-transform:uppercase;
               color:var(--ink-3); }
.c-stat b { display:block; font-size:var(--text-2xl); font-weight:750;
            line-height:1.15; margin:3px 0 2px;
            font-variant-numeric:tabular-nums; }
.c-stat i { font-style:normal; font-size:var(--text-sm); color:var(--ink-3); }

.c-bar { height:4px; border-radius:var(--r-pill); background:var(--line-soft);
         overflow:hidden; }
.c-bar i { display:block; height:100%; border-radius:var(--r-pill);
           background:var(--gold); }
.c-bar.c-t-good i { background:var(--good); }
.c-bar.c-t-mid i  { background:var(--mid); }
.c-bar.c-t-bad i  { background:var(--bad); }

.c-score { display:grid; grid-template-columns:1fr auto; gap:3px 10px;
           align-items:baseline; font-size:var(--text-base); }
.c-score i { font-style:normal; font-size:var(--text-sm); color:var(--ink-3);
              margin-left:7px; }
.c-score b { font-family:var(--font-mono); font-size:var(--text-sm); }
.c-score .c-bar { grid-column:1 / -1; }

.c-avatar { width:44px; height:44px; border-radius:13px; display:grid;
            place-items:center; color:#FFF9F0; font-family:var(--font-serif);
            font-size:19px; flex:none; }

/* ── calendar ─────────────────────────────────────────── */
.c-cal { border:1px solid var(--line); border-radius:var(--r-card);
         background:var(--surface); overflow:hidden; }
.c-cal-h { display:flex; align-items:baseline; gap:10px; flex-wrap:wrap;
           padding:14px 16px; border-bottom:1px solid var(--line); }
.c-cal-h b { font-size:var(--text-lg); font-weight:750; letter-spacing:-.01em; }
.c-cal-h span { font-family:var(--font-mono); font-size:var(--text-xs);
                color:var(--gold-dk); }
.c-cal-g { display:grid; grid-template-columns:repeat(7,1fr);
           gap:1px; background:var(--line-soft); padding:1px; }
.c-cal-wd { background:var(--surface-2); padding:7px 4px; text-align:center;
            font-family:var(--font-mono); font-size:9.5px; letter-spacing:.08em;
            text-transform:uppercase; color:var(--ink-3); }
.c-cal-pad { background:var(--surface); }
.c-cal-d { background:var(--surface); padding:7px 6px 8px; min-height:62px;
           display:flex; flex-direction:column; gap:1px; }
.c-cal-d b { font-size:var(--text-md); font-weight:700; line-height:1.1; }
.c-cal-t { font-style:normal; font-family:var(--font-mono); font-size:9.5px;
           color:var(--ink-3); }
/* Krishna paksha is the waning half — tinted so the fortnight reads at a
   glance, which is how a printed panchang does it. */
.c-cal-krishna { background:var(--surface-2); }
.c-cal-fest b { color:var(--gold-dk); }
.c-cal-f { font-style:normal; font-size:9px; line-height:1.25;
           color:var(--gold-dk); font-weight:650; margin-top:2px;
           overflow:hidden; }
.c-cal-r { font-style:normal; font-size:9px; line-height:1.25;
           color:var(--ink-3); margin-top:2px; }
.c-cal-today { box-shadow:inset 0 0 0 2px var(--gold); }
/* A cell that goes somewhere says so. Only the cells with a page behind
   them get this — the rest are the same numbers, unlinked, because the
   archive starts at launch and a month before it is real days with no
   page. */
.c-cal-go { text-decoration:none; color:inherit;
            transition:background .14s, box-shadow .14s; }
.c-cal-go:hover { background:var(--gold-bg); }
.c-cal-go:focus-visible { outline:2px solid var(--gold-dk); outline-offset:-2px; }
@media (prefers-reduced-motion:reduce) { .c-cal-go { transition:none; } }
/* The arrows push to the far end of the header row, away from the month
   name and the masa beside it. */
.c-cal-step { margin-left:auto; display:flex; gap:2px; }
.c-cal-step a, .c-cal-step > span { display:grid; place-items:center;
            width:30px; height:30px; border-radius:8px; font-size:17px;
            line-height:1; text-decoration:none; }
.c-cal-step a { color:var(--ink); background:var(--surface-2);
                box-shadow:inset 0 0 0 1px var(--line); }
.c-cal-step a:hover { background:var(--gold-bg);
                      box-shadow:inset 0 0 0 1px var(--gold); }
.c-cal-step a:focus-visible { outline:2px solid var(--gold-dk);
                              outline-offset:2px; }
/* Present but dead, rather than absent: the header keeps one shape whether
   or not there is a month on the other side. */
.c-cal-step > span { color:var(--ink-3); opacity:.4; }
.c-cal-key { display:flex; flex-wrap:wrap; gap:12px; padding:11px 16px;
             border-top:1px solid var(--line); font-size:var(--text-xs);
             color:var(--ink-3); }
.c-cal-key span { display:flex; align-items:center; gap:5px; }
.c-cal-sw { width:10px; height:10px; border-radius:3px; background:var(--surface);
            border:1px solid var(--line); }
.c-cal-kr { background:var(--surface-2); }
.c-cal-fw { background:var(--gold-bg); border-color:var(--gold); }
@media (max-width:560px) {
  .c-cal-d { min-height:54px; padding:5px 4px 6px; }
  .c-cal-f, .c-cal-r { font-size:8px; }
}

/* ── inputs ───────────────────────────────────────────── */
.c-sel-w select { width:100%; min-height:46px; padding:0 34px 0 12px;
                  font-size:var(--text-md); font-family:inherit; color:var(--ink);
                  background:var(--surface); border:1px solid var(--line);
                  border-radius:10px; -webkit-appearance:none; appearance:none;
                  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                                   linear-gradient(135deg,currentColor 50%,transparent 50%);
                  background-position:calc(100% - 18px) 21px,calc(100% - 13px) 21px;
                  background-size:5px 5px,5px 5px; background-repeat:no-repeat; }
.c-sel-w select:focus { outline:2px solid var(--gold); outline-offset:1px; }
.c-compact { margin-bottom:0; }

.c-dob, .c-tob { margin-bottom:var(--space-3); }
.c-dob > .c-label, .c-tob > .c-label { margin-bottom:5px; }
/* Day narrower than month, year narrower than month — a birth date is read
   left to right and the month is the widest word. */
.c-dob-r { display:grid; grid-template-columns:.8fr 1.4fr .9fr; gap:8px; }
.c-tob-unk { padding:10px 12px 2px; }
/* The closed field now reads what the picker holds. Its spans are written by
   the same generated :checked rules as the readout inside the sheet, so the
   two can never disagree. */
.c-pf-face { display:flex; align-items:baseline; gap:0; flex:1; min-width:0;
             font-size:var(--text-md); font-weight:600; }
.c-pf-sep { opacity:.45; }
.c-pf-mer { margin-left:6px; font-size:13px; color:var(--ink-3); }
/* Once the time is unknown the chips are moot — noon is used — so the sheet
   shows the note instead of a picker the answer will ignore. */
.c-tob-unk input[type="checkbox"].c-radio:checked ~ .c-unknown { }
.c-pf-c:has(.c-tob-unk input:checked) .c-tp { display:none; }
/* AND A FIELD NOBODY HAS TOUCHED MUST NOT LOOK BROKEN EITHER. .c-pf-face
   blanks the sheet's `--` dashes on purpose, on the reasoning that every
   caller hands this component a value — /home/matching/ does not, because
   the other person's birth time is the thing it is asking for. With no hour
   chosen the whole closed field was a lone colon: exactly the "reads as a
   broken picker rather than an empty one" the dashes exist to prevent, one
   element further out. popover_field is already told to say "Choose a time";
   until an hour is picked, the face stands aside and lets it. */
.c-pf:has(.c-tp):not(:has(input[name$="-h"]:checked)) .c-pf-face {
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip-path:inset(50%); }
.c-pf:has(.c-tp):not(:has(input[name$="-h"]:checked)) .c-pf-t::before {
  content:'Choose a time'; flex:1; min-width:0;
  font-size:var(--text-md); font-weight:600; color:var(--ink-3); }

/* And the closed field shows the time the reading will actually use. Noon is
   not a placeholder here — readBirth() sends 12:00 and the chart is cast for
   it — so the field says 12:00 PM rather than describing itself. Whatever
   was on the dial before the box was ticked is not what gets sent, which is
   why the face is covered rather than left showing it. */
.c-pf:has(.c-tob-unk input:checked) .c-pf-face { position:absolute;
  width:1px; height:1px; overflow:hidden; clip-path:inset(50%); }
.c-pf:has(.c-tob-unk input:checked) .c-pf-t::before {
  content:'12:00 PM'; flex:1; min-width:0;
  font-size:var(--text-md); font-weight:600; color:var(--ink-2); }

/* ── example caption ──────────────────────────────────── */
.c-eg-line { display:flex; flex-wrap:wrap; align-items:center;
             justify-content:center; gap:4px 8px;
             font-family:var(--font-mono); font-size:10px; letter-spacing:.1em;
             text-transform:uppercase; color:var(--ink-3); margin:14px 0 0; }
.c-eg-line b { color:var(--ink-2); }
.c-eg { padding:3px 8px; border-radius:var(--r-pill); background:var(--gold-bg);
        border:1px solid var(--gold-lt); color:var(--gold-dk); }
/* Once a real input arrives the badge stops being a badge — the words stay as
   a caption, the chip goes. */
.c-eg-off { padding:0; background:none; border:0; color:var(--ink-3); }
.c-eg-line i { flex-basis:100%; font-style:normal; text-align:center;
               letter-spacing:.07em; color:var(--ink-3); }

.c-unknown { display:flex; align-items:center; gap:9px; min-height:44px;
             cursor:pointer; font-size:var(--text-base); color:var(--ink-2); }
.c-unknown-p { display:none; }
input[type="checkbox"].c-radio:checked ~ .c-unknown-p { display:block; }
input[type="checkbox"].c-radio:checked ~ .c-unknown .c-box { background:var(--gold-solid);
    border-color:var(--gold-solid); }
input[type="checkbox"].c-radio:checked ~ .c-unknown .c-box::after { opacity:1; }

.c-optg { display:grid; gap:7px; }
/* One option is one column, at every width. The two-column rule is keyed to
   the VIEWPORT, so a single checkbox inside a 400px popover on a desktop was
   given half of 400px and wrapped to two lines. */
.c-optg-1 { grid-template-columns:1fr; }
.c-optg-2 { grid-template-columns:1fr; }
@media (min-width:560px) { .c-optg-2 { grid-template-columns:repeat(2,1fr); } }
/* Pills: the same options, sized to their labels and wrapping. A grid of
   equal columns would set "Other" as wide as "Feature Request" and undo the
   only thing this layout is for. */
.c-optg-p { display:flex; flex-wrap:wrap; gap:8px; }
.c-optg-p .c-opt { border-radius:var(--r-pill); padding:0 16px 0 13px; }
.c-opt { display:flex; align-items:center; gap:10px; min-height:44px;
         padding:0 13px; cursor:pointer; border:1px solid var(--line);
         border-radius:var(--r-in); background:var(--surface);
         font-size:var(--text-base); }
.c-opt:hover { border-color:var(--gold-lt); }
.c-radio-v { position:absolute; opacity:0; width:0; height:0; }
.c-dot, .c-box { flex:none; width:19px; height:19px; border:1.5px solid var(--line);
                 background:var(--surface); position:relative; }
.c-dot { border-radius:50%; }
.c-box { border-radius:5px; }
.c-dot::after { content:''; position:absolute; inset:4px; border-radius:50%;
                background:var(--gold-solid); opacity:0; }
.c-box::after { content:'✓'; position:absolute; inset:0; display:grid;
                place-items:center; font-size:12px; font-weight:700;
                color:var(--gold-solid-text); opacity:0; }
.c-radio-v:checked ~ .c-dot { border-color:var(--gold-solid); }
.c-radio-v:checked ~ .c-dot::after { opacity:1; }
.c-radio-v:checked ~ .c-box { background:var(--gold-solid); border-color:var(--gold-solid); }
.c-radio-v:checked ~ .c-box::after { opacity:1; }
.c-radio-v:focus-visible ~ .c-dot, .c-radio-v:focus-visible ~ .c-box {
    outline:2px solid var(--gold); outline-offset:2px; }
.c-opt:has(.c-radio-v:checked) { border-color:var(--gold); background:var(--gold-bg); }

.c-ta { position:relative; }
.c-ta textarea { width:100%; padding:12px 13px; font-size:var(--text-md);
                 font-family:inherit; line-height:1.55; color:var(--ink);
                 background:var(--surface); border:1px solid var(--line);
                 border-radius:10px; resize:vertical; }
.c-ta textarea:focus { outline:2px solid var(--gold); outline-offset:1px; }
.c-count { position:absolute; right:11px; bottom:9px; font-family:var(--font-mono);
           font-size:9.5px; color:var(--ink-3); }

.c-search { display:flex; align-items:center; gap:9px; padding:0 13px;
            min-height:46px; border:1px solid var(--line); border-radius:10px;
            background:var(--surface); }
.c-search-i { color:var(--ink-3); font-size:15px; }
.c-search input { flex:1; min-width:0; border:0; background:none; outline:none;
                  font-size:var(--text-md); font-family:inherit; color:var(--ink);
                  min-height:44px; }

.c-stepper { display:flex; align-items:center; border:1px solid var(--line);
             border-radius:10px; background:var(--surface); overflow:hidden; }
.c-step { display:grid; place-items:center; width:46px; min-height:46px;
          cursor:pointer; font-size:17px; color:var(--ink-2); flex:none;
          background:var(--surface-2); }
.c-step:hover { color:var(--gold-dk); }
.c-stepper input { flex:1; min-width:0; border:0; background:none; outline:none;
                   text-align:center; font-family:var(--font-mono);
                   font-size:var(--text-md); color:var(--ink); min-height:46px; }
.c-stepper em { font-style:normal; font-size:var(--text-sm); color:var(--ink-3);
                padding-right:10px; }

/* ── forms ────────────────────────────────────────────── */
.c-form { background:var(--surface); border:1px solid var(--line);
          border-radius:var(--r-card); padding:20px; }
@media (min-width:760px) { .c-form { padding:24px 26px; } }
.c-form-h { margin-bottom:var(--space-4); }
.c-form-h b { display:block; font-size:var(--text-lg); font-weight:750; }
.c-form-h span { display:block; font-size:var(--text-sm); color:var(--ink-3);
                 margin-top:2px; }
.c-form-a { margin-top:var(--space-2); }
.c-frow { display:grid; gap:10px; }
.c-frow-2 { grid-template-columns:1fr; }
@media (min-width:560px) { .c-frow-2 { grid-template-columns:repeat(2,1fr); } }

/* the signed-in state: a summary, not five empty boxes */
.c-known { border:1px solid var(--gold); background:var(--gold-bg);
           border-radius:var(--r-card); padding:16px 18px; }
.c-known-h { display:flex; align-items:baseline; justify-content:space-between;
             gap:12px; }
.c-known-e { font-size:var(--text-sm); font-weight:700; color:var(--gold-dk);
             cursor:pointer; min-height:44px; display:flex; align-items:center; }
.c-known-n { font-size:var(--text-xl); font-weight:750; letter-spacing:-.01em;
             margin-top:2px; }
.c-known-d { font-family:var(--font-mono); font-size:var(--text-sm);
             color:var(--ink-2); margin-top:3px; }
.c-known-c { display:flex; flex-wrap:wrap; gap:6px; margin-top:11px; }

/* ── south indian chart ───────────────────────────────── */
.c-sc { display:grid; grid-template-columns:repeat(4,1fr);
        grid-template-rows:repeat(4,1fr); gap:1px; background:var(--line);
        border:1px solid var(--line); border-radius:var(--r-in);
        overflow:hidden; aspect-ratio:1; max-width:340px; }
.c-sc-c { background:var(--surface); padding:5px 6px; display:flex;
          flex-direction:column; gap:2px; min-width:0; }
.c-sc-c > span { font-family:var(--font-mono); font-size:9px; color:var(--ink-3); }
.c-sc-p { display:flex; flex-wrap:wrap; gap:3px; }
.c-sc-p i { font-style:normal; font-family:var(--font-mono); font-size:10.5px;
            font-weight:700; color:var(--ink); }
/* The ascendant cell is marked with a corner rule, as a hand-drawn chart does. */
.c-sc-asc { background:var(--gold-bg); box-shadow:inset 0 0 0 1.5px var(--gold); }
.c-sc-asc > span { color:var(--gold-dk); font-weight:700; }
.c-sc-mid { grid-column:2 / 4; grid-row:2 / 4; background:var(--surface-2);
            display:grid; place-items:center; text-align:center; padding:8px;
            font-family:var(--font-mono); font-size:10px; color:var(--ink-3);
            line-height:1.5; }

/* ── north indian chart ───────────────────────────────── */
.c-nc { width:100%; max-width:340px; aspect-ratio:1; display:block; }
.c-nc-bg { fill:var(--line); }
.c-nc-r { fill:var(--surface); }
/* The rashi numbers were 11px and the grahas 10px, which is backwards: the
   numbers are the frame and the grahas are what you came to read, and the
   frame was set a size larger than the content sitting next to it. */
.c-nc-s { font-family:var(--font-mono); font-size:9.5px; fill:var(--ink-3);
          text-anchor:middle; }
.c-nc-p { font-family:var(--font-mono); font-size:11px; font-weight:700;
          fill:var(--ink); text-anchor:middle; }
.c-nc-asc { fill:var(--gold-dk); }
/* vakri. Small, and in the ink the rest of the name is in — a retrograde
   graha is not a warning, it is a state, and colouring it would say
   otherwise. */
.c-nc-rx { font-size:6.5px; fill:var(--ink-3); }

/* ── navigation ───────────────────────────────────────── */
.c-tabs { display:flex; gap:6px; flex-wrap:wrap; }
.c-tab { display:grid; place-items:center; min-height:44px; padding:0 16px;
         border-radius:var(--r-pill); cursor:pointer; font-size:var(--text-base);
         font-weight:650; border:1px solid var(--line); background:var(--surface);
         color:var(--ink-2); white-space:nowrap; }
.c-tab:hover { color:var(--ink); }

.c-crumb { display:flex; align-items:center; gap:7px; flex-wrap:wrap;
           font-size:var(--text-base); color:var(--ink-3); }
/* 44px, like every other link a thumb has to find. These were 22px from the
   day the component was written — a breadcrumb reads as text, so nothing ever
   measured it as a target. The negative margin keeps the row visually the same
   height it was; only the hit area grows. */
.c-crumb a, .c-crumb b { display:inline-flex; align-items:center;
           min-height:44px; margin-block:-11px; color:var(--ink-3); }
.c-crumb a { color:var(--ink-3); }
.c-crumb a:hover { color:var(--gold-dk); }
.c-crumb b { color:var(--ink); font-weight:650; }

.c-pg { display:flex; gap:4px; flex-wrap:wrap; align-items:center; }
.c-pg-n { display:grid; place-items:center; min-width:44px; min-height:44px;
          padding:0 8px; border-radius:10px; font-family:var(--font-mono);
          font-size:var(--text-base); border:1px solid var(--line);
          background:var(--surface); color:var(--ink-2); }
.c-pg-n:hover { border-color:var(--gold-lt); color:var(--ink); }
.c-pg-n.c-on { background:var(--pill-ink); border-color:var(--pill-ink);
               color:var(--pill-text); font-weight:700; }
.c-pg-gap { padding:0 4px; color:var(--ink-3); }

.c-bn { display:flex; background:var(--surface);
        border-top:1px solid var(--line); }
.c-bn-i { flex:1; display:flex; flex-direction:column; align-items:center;
          justify-content:center; gap:3px; min-height:56px; padding:6px 2px;
          font-size:10.5px; font-weight:600; color:var(--ink-3); min-width:0; }
.c-bn-g { font-size:19px; line-height:1; font-family:var(--font-glyph);
          font-variant-emoji:text; }
.c-bn-i.c-on { color:var(--gold-dk); }
.c-bn-i.c-on .c-bn-g { color:var(--gold-dk); }

/* ── feedback ─────────────────────────────────────────── */
.c-alert { display:flex; gap:11px; align-items:flex-start; padding:13px 15px;
           border-radius:var(--r-in); font-size:var(--text-base);
           line-height:1.55; background:var(--surface-2); color:var(--ink-2);
           border:1px solid var(--line); }
.c-alert b { display:block; color:var(--ink); margin-bottom:2px; }
.c-al-m { flex:none; width:20px; height:20px; border-radius:50%; display:grid;
          place-items:center; font-size:12px; font-weight:700;
          background:var(--line); color:var(--ink); }
.c-alert.c-t-good { background:var(--good-bg); border-color:transparent; }
.c-alert.c-t-good .c-al-m { background:var(--good); color:var(--surface); }
.c-alert.c-t-mid { background:var(--mid-bg); border-color:transparent; }
.c-alert.c-t-mid .c-al-m { background:var(--mid); color:var(--surface); }
.c-alert.c-t-bad { background:var(--bad-bg); border-color:transparent; }
.c-alert.c-t-bad .c-al-m { background:var(--bad); color:var(--surface); }
.c-alert > div { flex:1; min-width:0; color:var(--ink-2); }
.c-al-x { flex:none; cursor:pointer; color:var(--ink-3); font-size:18px;
          line-height:1; padding:2px 4px; }

.c-es { text-align:center; padding:34px 20px; border:1px dashed var(--line);
        border-radius:var(--r-card); background:var(--surface); }
/* --gold-lt is a border tint, not an ink. At 30px on white it measured
   1.60:1 — invisible to anyone with low vision, and it is the only thing
   drawing the eye to an empty state. */
.c-es-g { display:block; font-size:30px; color:var(--gold-dk); line-height:1;
          margin-bottom:10px; }
.c-es b { display:block; font-size:var(--text-lg); font-weight:700; }
.c-es p { max-width:44ch; margin:6px auto 0; font-size:var(--text-base);
          color:var(--ink-3); }
.c-es-a { margin-top:16px; display:flex; justify-content:center; }

.c-sk { display:flex; flex-direction:column; gap:9px; }
.c-sk-l { height:12px; border-radius:6px; background:var(--line-soft);
          animation:c-pulse 1.6s ease-in-out infinite; }
@keyframes c-pulse { 0%,100% { opacity:1; } 50% { opacity:.45; } }
@media (prefers-reduced-motion:reduce) { .c-sk-l { animation:none; } }

.c-pr-l { display:flex; justify-content:space-between; gap:12px;
          font-size:var(--text-sm); color:var(--ink-3); margin-bottom:6px; }
.c-pr-l b { font-family:var(--font-mono); color:var(--ink); }
.c-pr-t { height:7px; border-radius:var(--r-pill); background:var(--line-soft);
          overflow:hidden; }
.c-pr-t i { display:block; height:100%; border-radius:var(--r-pill);
            background:var(--gold); }
.c-pr-t i.c-t-good { background:var(--good); }
.c-pr-t i.c-t-mid { background:var(--mid); }

.c-spin { display:inline-block; width:20px; height:20px; }
.c-spin i { display:block; width:100%; height:100%; border-radius:50%;
            border:2px solid var(--line); border-top-color:var(--gold-dk);
            animation:c-rot .8s linear infinite; }
.c-spin-cur i { border-color:currentColor; border-top-color:transparent; }
@keyframes c-rot { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion:reduce) {
  .c-spin i { animation:none; border-top-color:var(--gold-dk); }
  /* Still a ring, just not a turning one — a gap that never moves reads as a
     broken circle rather than as waiting. */
  .c-spin-cur i { border-top-color:currentColor; } }

/* ── overlay, checkbox-driven ─────────────────────────── */
.c-mo-t { display:inline-block; cursor:pointer; }
.c-mo, .c-bs { display:none; position:fixed; inset:0; z-index:100; }
.c-radio:checked ~ .c-mo, .c-radio:checked ~ .c-bs { display:block; }
.c-mo-bg { position:absolute; inset:0; background:rgba(10,7,3,.55); cursor:pointer; }
.c-mo-p { position:relative; max-width:460px; margin:8vh auto 0;
          background:var(--surface); border:1px solid var(--line);
          border-radius:var(--r-card); padding:20px 22px;
          box-shadow:0 20px 60px rgba(10,7,3,.35); }
.c-mo-h { display:flex; align-items:baseline; justify-content:space-between;
          gap:12px; margin-bottom:10px; }
.c-mo-h b { font-size:var(--text-lg); font-weight:750; }
.c-mo-x { cursor:pointer; font-size:22px; line-height:1; color:var(--ink-3);
          min-width:44px; min-height:44px; display:grid; place-items:center; }
.c-mo-b { font-size:var(--text-base); line-height:1.6; color:var(--ink-2); }
.c-mo-a { display:flex; gap:9px; justify-content:flex-end; margin-top:18px;
          flex-wrap:wrap; }
.c-bs-p { position:absolute; left:0; right:0; bottom:0; background:var(--surface);
          border-top:1px solid var(--line); border-radius:20px 20px 0 0;
          padding:10px 20px 26px; max-height:80vh; overflow-y:auto; }
.c-bs-g { display:block; width:38px; height:4px; border-radius:2px;
          background:var(--line); margin:0 auto 14px; }
.c-bs-p b { display:block; font-size:var(--text-lg); font-weight:750;
            margin-bottom:8px; }

.c-tip { position:relative; border-bottom:1px dotted var(--ink-3);
         cursor:help; outline-offset:3px; }
.c-tip-p { position:absolute; left:50%; bottom:100%; transform:translateX(-50%);
           margin-bottom:8px; width:max-content; max-width:250px; padding:9px 12px;
           border-radius:9px; background:var(--pill-ink); color:var(--pill-text);
           font-size:var(--text-sm); line-height:1.5; opacity:0;
           pointer-events:none; transition:opacity .12s; z-index:20; }
.c-tip:hover .c-tip-p, .c-tip:focus .c-tip-p { opacity:1; }

/* ── astrology ────────────────────────────────────────── */
.c-pl { display:grid; grid-template-columns:1fr 1fr .5fr 1.1fr .8fr .9fr;
        gap:10px; padding:9px 0; border-bottom:1px solid var(--line-soft);
        font-size:var(--text-base); color:var(--ink-2); align-items:baseline; }
.c-pl b { color:var(--ink); font-weight:700; }
.c-pl-h, .c-pl-d { font-family:var(--font-mono); font-size:var(--text-sm); }
@media (max-width:640px) {
  .c-pl { grid-template-columns:1fr 1fr; gap:2px 10px; padding:11px 0; }
  .c-pl b { grid-column:1 / -1; font-size:var(--text-md); }
}

.c-dt { display:flex; gap:2px; border-radius:var(--r-in); overflow:hidden; }
.c-dt-p { min-width:0; padding:9px 8px; background:var(--surface-2);
          border-top:2px solid var(--line); }
.c-dt-p b { display:block; font-size:var(--text-sm); font-weight:700;
            white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.c-dt-p i { display:block; font-style:normal; font-family:var(--font-mono);
            font-size:9.5px; color:var(--ink-3); }
.c-dt-p.c-on { background:var(--gold-bg); border-top-color:var(--gold); }
.c-dt-p.c-on b { color:var(--gold-dk); }

.c-zp { display:grid; grid-template-columns:repeat(3,1fr); gap:7px; }
@media (min-width:600px) { .c-zp { grid-template-columns:repeat(6,1fr); } }
/* The card is the tap target and it holds four lines, so it sets its own
   height rather than inheriting the 44px floor and then overflowing. */
.c-rc { display:flex; flex-direction:column; align-items:center; gap:1px;
        min-height:96px; justify-content:center; padding:11px 5px; cursor:pointer;
        border:1px solid var(--line); border-radius:var(--r-in);
        background:var(--surface); text-align:center; }
.c-rc:hover { border-color:var(--gold-lt); }
.c-rc b { font-size:var(--text-sm); font-weight:650; margin-top:4px; }
.c-rc i { font-style:normal; font-size:10.5px; color:var(--ink-3); }
.c-rc u { text-decoration:none; font-size:12px; color:var(--gold-dk); }
.c-rc.c-on { border-color:var(--gold); background:var(--gold-bg); }
/* The row symbols sit inline with their label, so they need the baseline
   nudge and a gap that the standalone card does not. */
.c-pl b .c-sym, .c-pl span .c-sym { width:17px; height:17px; margin-right:6px;
        vertical-align:-.24em; color:var(--ink-3); }

.c-spark { width:120px; height:30px; }
.c-spark-l { fill:none; stroke:var(--gold); stroke-width:1.5;
             stroke-linejoin:round; vector-effect:non-scaling-stroke; }
.c-spark-f { fill:var(--gold-bg); }
.c-spark-e { fill:var(--gold-dk); }
.c-spark.c-t-good .c-spark-l { stroke:var(--good); }
.c-spark.c-t-good .c-spark-e { fill:var(--good); }

/* ── commerce ─────────────────────────────────────────── */
.c-pk { position:relative; padding:18px 14px; text-align:center;
        border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); }
.c-pk.c-on { border-color:var(--gold); background:var(--gold-bg); }
.c-pk b { display:block; font-size:var(--text-2xl); font-weight:750;
          letter-spacing:-.02em; }
.c-pk span { display:block; font-size:var(--text-sm); color:var(--ink-3);
             margin-top:2px; }
.c-pk i { display:block; font-style:normal; font-family:var(--font-mono);
          font-size:10px; color:var(--ink-3); margin-top:6px; line-height:1.5; }
/* .c-pk .c-pk-t, not .c-pk-t — the element selector `.c-pk span` scores
   (0,1,1) and was beating the badge's own (0,1,0) class, so "most chosen"
   rendered in muted grey on gold at 1.44:1. Specificity collisions inside one
   component are the easiest kind to miss and the contrast test is what caught
   it. */
.c-pk .c-pk-t { position:absolute; top:-9px; left:50%; transform:translateX(-50%);
          font-family:var(--font-mono); font-size:9px; letter-spacing:.08em;
          text-transform:uppercase; padding:3px 9px; border-radius:var(--r-pill);
          background:var(--gold-solid); color:var(--gold-solid-text);
          white-space:nowrap; }

.c-wal { display:inline-flex; align-items:center; gap:7px; min-height:44px;
         padding:0 14px; border-radius:var(--r-pill); border:1px solid var(--gold);
         background:var(--gold-bg); }
.c-wal-i { font-style:normal; color:var(--gold-dk); }
.c-wal b { font-family:var(--font-mono); font-size:var(--text-md);
           font-weight:700; font-variant-numeric:tabular-nums; }
.c-wal em { font-style:normal; font-size:10.5px; font-weight:700;
            color:var(--good); }

.c-meter { display:flex; align-items:center; justify-content:space-between;
           gap:14px; padding:11px 15px; border-radius:var(--r-in);
           background:var(--surface-2); border:1px solid var(--line); }
.c-meter-l { display:flex; align-items:center; gap:8px;
             font-size:var(--text-sm); color:var(--ink-2); }
.c-meter-d { width:8px; height:8px; border-radius:50%; background:var(--bad);
             animation:c-pulse 1.6s ease-in-out infinite; }
@media (prefers-reduced-motion:reduce) { .c-meter-d { animation:none; } }
.c-meter-r { text-align:right; }
.c-meter-r b { display:block; font-family:var(--font-mono);
               font-size:var(--text-lg); font-weight:700; }
.c-meter-r i { font-style:normal; font-size:10.5px; color:var(--ink-3); }

/* ── chat ─────────────────────────────────────────────── */
.c-msg { max-width:78%; font-size:var(--text-base); line-height:1.6;
         padding:11px 14px; border-radius:14px; background:var(--surface-2);
         margin-bottom:9px; color:var(--ink-2); }
.c-msg-me { margin-left:auto; background:var(--gold-bg); color:var(--ink); }
.c-msg-m { font-family:var(--font-mono); font-size:9.5px; letter-spacing:.07em;
           color:var(--ink-3); margin-bottom:4px; }

.c-th { display:flex; gap:11px; align-items:center; padding:11px;
        border-radius:13px; cursor:pointer; border:1px solid transparent;
        min-height:44px; }
.c-th:hover { background:var(--surface-2); }
.c-th-b { flex:1; min-width:0; display:flex; flex-direction:column;
          line-height:1.35; }
.c-th-b b { font-size:var(--text-base); }
.c-th-b i { font-style:normal; font-size:var(--text-sm); color:var(--ink-3);
            white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.c-th-r { display:flex; flex-direction:column; align-items:flex-end; gap:4px; }
.c-th-r em { font-style:normal; font-family:var(--font-mono); font-size:10px;
             color:var(--ink-3); }
.c-th-r .c-th-u { background:var(--gold-solid); color:var(--gold-solid-text);
          border-radius:var(--r-pill); min-width:18px; height:18px;
          display:grid; place-items:center; font-size:10px; font-weight:700; }

.c-comp { display:flex; gap:9px; align-items:center; }
.c-comp input { flex:1; min-width:0; min-height:46px; padding:0 14px;
                font-size:var(--text-md); font-family:inherit; color:var(--ink);
                background:var(--surface); border:1px solid var(--line);
                border-radius:var(--r-pill); }
.c-comp input:focus { outline:2px solid var(--gold); outline-offset:1px; }

/* ── theme toggle ─────────────────────────────────────── */
.c-th-t { display:grid; place-items:center; width:34px; height:34px; flex:none;
          cursor:pointer; border:1px solid var(--line); border-radius:10px;
          background:var(--surface); color:var(--ink-2); }
/* 34px is a comfortable mouse target and an uncomfortable thumb one. This is
   the only control on the site that was under 44px on both edges — everything
   else that measured small is a visually hidden checkbox whose <label> is the
   real target. Widened by pointer, not by width, because a 44px square is
   right for a finger and slightly clumsy under a cursor. */
@media (pointer:coarse) {
  .c-th-t { width:44px; height:44px; border-radius:12px; }
}
.c-th-t:hover { color:var(--gold-dk); border-color:var(--gold-lt); }
.c-th-t svg { width:18px; height:18px; fill:none; stroke:currentColor;
              stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.c-th-t svg circle { fill:none; }

/* ── validation ───────────────────────────────────────── */
.c-req { color:var(--bad); text-decoration:none; margin-left:3px;
         font-size:11px; }
.c-help { display:block; font-size:var(--text-sm); color:var(--ink-3);
          margin-top:5px; line-height:1.45; }
/* Helper text is a <span>, so the shell's `.d-main p a` never reached it and
   the first link anybody put in one came out in Chrome's blue — then purple
   once it had been clicked, on a page with no blue and no purple in it. */
.c-help a { color:var(--gold-dk); text-underline-offset:2px; }
.c-err { display:flex; align-items:flex-start; gap:6px; font-size:var(--text-sm);
         color:var(--bad); margin-top:5px; line-height:1.45; }
.c-err i { flex:none; font-style:normal; width:15px; height:15px;
           border-radius:50%; background:var(--bad); color:var(--surface);
           display:grid; place-items:center; font-size:10px; font-weight:700;
           margin-top:1px; }
.c-invalid input, .c-invalid select, .c-invalid textarea { border-color:var(--bad); }
.c-invalid input:focus, .c-invalid select:focus, .c-invalid textarea:focus {
    outline-color:var(--bad); }

/* ── fieldset ─────────────────────────────────────────── */
.c-fs { border:1px solid var(--line); border-radius:var(--r-card);
        padding:16px 18px 4px; margin:0 0 var(--space-3); }
/* THE 4px BOTTOM ASSUMES THE LAST THING INSIDE BRINGS ITS OWN MARGIN, which
   .c-field does and an option group does not. A fieldset holding nothing but
   a row of options sat 16px under its legend and 5px off its own bottom
   edge — the options bottom-heavy in a box drawn around them. */
.c-fs:has(> .c-optg:last-child) { padding-bottom:16px; }
.c-fs legend { padding:0 7px; font-family:var(--font-mono); font-size:10px;
               letter-spacing:.12em; text-transform:uppercase; color:var(--ink-3); }
.c-fs-h { display:block; font-size:var(--text-sm); color:var(--ink-3);
          margin:0 0 var(--space-3); }

/* ── otp ──────────────────────────────────────────────── */
.c-otp { display:flex; gap:8px; }
.c-otp-b { flex:1; min-width:0; max-width:56px; height:56px; text-align:center;
           font-family:var(--font-mono); font-size:var(--text-xl); font-weight:700;
           color:var(--ink); background:var(--surface);
           border:1px solid var(--line); border-radius:10px; }
.c-otp-b:focus { outline:2px solid var(--gold); outline-offset:1px;
                 border-color:var(--gold); }

/* ── date picker ──────────────────────────────────────── */
.c-dp { border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); overflow:hidden; max-width:360px; }
.c-dp-h { display:flex; align-items:center; gap:8px; padding:9px 10px;
          border-bottom:1px solid var(--line); }
.c-dp-sel { flex:1; min-width:0; display:grid; grid-template-columns:1.5fr 1fr;
            gap:6px; }
.c-dp-sel .c-field { margin-bottom:0; }
/* 44px, not 40. These two dropdowns ARE the picker's primary control — they
   are how you reach 1990 — so shaving four pixels to tighten the header was
   the wrong trade. */
.c-dp-sel select { min-height:44px; font-size:var(--text-base); padding:0 26px 0 9px;
                   background-position:calc(100% - 14px) 20px,calc(100% - 9px) 20px; }
.c-dp-nav { display:grid; place-items:center; width:44px; height:44px; flex:none;
            border:1px solid var(--line); border-radius:9px; cursor:pointer;
            background:var(--surface); color:var(--ink-2); padding:0; }
.c-dp-nav:hover { border-color:var(--gold); color:var(--gold-dk); }
.c-dp-nav svg { width:17px; height:17px; }
.c-dp-g { display:grid; grid-template-columns:repeat(7,1fr); gap:2px; padding:7px; }
.c-dp-wd { text-align:center; font-family:var(--font-mono); font-size:9px;
           letter-spacing:.06em; color:var(--ink-3); padding:2px 0 5px; }
/* A day is a <label> in the CSS-only picker and a <button> in live mode, so
   the shared rule has to clear the UA button styling as well. */
.c-dp-d { position:relative; display:flex; flex-direction:column;
          align-items:center; justify-content:center; gap:1px; min-height:44px;
          border-radius:10px; cursor:pointer; border:1px solid transparent;
          background:none; font:inherit; color:inherit; padding:0; width:100%; }
.c-dp-d:hover { background:var(--surface-2); }
.c-dp-d b { font-size:var(--text-base); font-weight:650; line-height:1.05; }
.c-dp-d em { font-style:normal; font-family:var(--font-mono); font-size:8.5px;
             color:var(--ink-3); line-height:1; }
.c-dp-f { width:4px; height:4px; border-radius:50%; background:var(--gold-dk);
          margin-top:1px; }
.c-dp-today { box-shadow:inset 0 0 0 1.5px var(--gold); }
/* The selected day must be unmistakable — solid fill, not a tint. The previous
   version had a selected state that never appeared at all, because the :checked
   rules are generated per group and nobody had generated them for this one.
   date_picker_css() now ships with the component so that cannot happen. */
.c-dp-on { background:var(--gold-solid) !important; }
.c-dp-on b, .c-dp-on em { color:var(--gold-solid-text); }
.c-dp-on .c-dp-f { background:var(--gold-solid-text); }
.c-dp-foot { display:flex; flex-wrap:wrap; gap:12px; padding:9px 12px;
             border-top:1px solid var(--line); font-size:var(--text-xs);
             color:var(--ink-3); }
.c-dp-k { display:flex; align-items:center; gap:5px; }
.c-dp-sw { width:10px; height:10px; border-radius:3px;
           box-shadow:inset 0 0 0 1.5px var(--gold); }

/* ── file upload ──────────────────────────────────────── */
.c-up { display:flex; align-items:center; gap:12px; padding:14px 15px;
        border:1px dashed var(--line); border-radius:var(--r-in);
        background:var(--surface); cursor:pointer; min-height:44px; }
.c-up:hover { border-color:var(--gold); }
.c-up input { position:absolute; opacity:0; width:0; height:0; }
.c-up-i { flex:none; width:34px; height:34px; border-radius:9px; display:grid;
          place-items:center; background:var(--gold-bg); color:var(--gold-dk);
          font-size:15px; }
.c-up b { display:block; font-size:var(--text-base); font-weight:650; }
.c-up i { display:block; font-style:normal; font-size:var(--text-sm);
          color:var(--ink-3); }

/* ── steps ────────────────────────────────────────────── */
.c-steps { display:flex; list-style:none; margin:0; padding:0; gap:0;
           overflow-x:auto; }
.c-steps-i { flex:1; min-width:0; display:flex; flex-direction:column;
          align-items:center; gap:5px; position:relative; padding:0 4px; }
.c-steps-i::before { content:''; position:absolute; top:13px; left:-50%;
                  width:100%; height:2px; background:var(--line); }
.c-steps-i:first-child::before { display:none; }
.c-steps-i span { position:relative; width:28px; height:28px; border-radius:50%;
               display:grid; place-items:center; font-family:var(--font-mono);
               font-size:12px; font-weight:700; background:var(--surface-2);
               color:var(--ink-3); border:1px solid var(--line); }
.c-steps-i i { font-style:normal; font-size:10.5px; color:var(--ink-3);
            text-align:center; line-height:1.3; }
.c-steps-i.c-done span { background:var(--good); color:var(--surface);
                      border-color:var(--good); }
.c-steps-i.c-done::before { background:var(--good); }
.c-steps-i.c-now span { background:var(--gold-solid); color:var(--gold-solid-text);
                     border-color:var(--gold-solid); }
.c-steps-i.c-now i { color:var(--ink); font-weight:650; }
.c-steps-n { font-family:var(--font-mono); font-size:var(--text-sm);
             color:var(--ink-3); text-align:center; margin:12px 0 0; }

/* ── reference visuals ────────────────────────────────── */
.c-mw { width:100%; max-width:340px; display:block; margin:0 auto; }
.c-mw-r, .c-mw-n { fill:var(--surface-2); stroke:var(--line); stroke-width:1; }
.c-mw-n { fill:var(--rail); }
.c-mw-r.c-mw-on { fill:var(--gold-solid); stroke:var(--gold); stroke-width:1.5; }
.c-mw-n.c-mw-on { fill:var(--gold-lt); stroke:var(--gold); stroke-width:1.5; }
.c-mw-g { font-family:var(--font-glyph); font-size:14px; fill:var(--ink-3); }
.c-mw-g.c-mw-on { fill:var(--gold-solid-text); font-size:17px; }
.c-mw-p { stroke:var(--gold-dk); stroke-width:2.5; }
.c-mw-t1 { font-family:var(--font-serif); font-size:20px; font-weight:600;
           fill:var(--ink); }
.c-mw-t2 { font-family:var(--font-mono); font-size:13px; fill:var(--gold-dk); }
.c-mw-t3 { font-family:var(--font-mono); font-size:9.5px; fill:var(--ink-3); }
/* ── THE MOON WHEEL IS SVG, SO NEITHER SIZE ABOVE IS THE SIZE DRAWN ──
   The 340-unit viewBox is mapped onto a box capped at 340px, so the
   multiplier runs 0.735 on a 320 screen up to 1.000 at its widest — it is
   never more than 1. .c-mw-t3 was drawn between 7.0px and 9.5px and never
   once reached the floor on any screen; .c-mw-t2 clears it everywhere except
   a 320, where 13px arrives as 9.6.
   Stepped by viewport, because nothing declares container-type here and the
   wheel's width tracks the viewport with a cap. Each step is spec x scale:
     >=480   11.5 x 1.000 = 11.5      <=389  16 x 0.735 = 11.8
     390-479 13   x 0.941 = 12.2 */
.c-mw-t3 { font-size:11.5px; }
@media (max-width:479px) { .c-mw-t3 { font-size:13px; } }
@media (max-width:389px) {
  .c-mw-t3 { font-size:16px; }
  .c-mw-t2 { font-size:16px; }
  /* The twelve rashi glyphs round the wheel. 14px x 0.735 is 10.3 — the one
     size on this drawing that clears the floor everywhere except the
     narrowest screen, which is exactly where a glyph matters most. */
  .c-mw-g { font-size:16px; }
}

.c-ring { width:100%; max-width:330px; display:block; margin:0 auto; }
.c-ring-s { fill:var(--surface-2); stroke:var(--line); stroke-width:1; }
/* A filled wedge, not a tint. --gold-bg is within a shade of the dark
   background, so the lit sign was invisible in the theme most people read
   this in — the whole point of the drawing, missing. */
.c-ring-s.c-ring-on { fill:var(--gold-solid); stroke:var(--gold); stroke-width:1.5; }
.c-ring-s.c-ring-alt { fill:var(--gold-bg); stroke:var(--gold); stroke-width:1.5; }
.c-ring-t.c-ring-alt { fill:var(--gold-dk); font-size:19px; }
.c-ring-arc { stroke:var(--gold); stroke-width:2; stroke-dasharray:3 3;
  opacity:.85; }
.c-ring-t { font-family:var(--font-glyph); font-size:15px; fill:var(--ink-3); }
.c-ring-t.c-ring-on { fill:var(--gold-dk); font-size:19px; }
.c-ring-m { font-family:var(--font-serif); font-size:21px; font-weight:600;
            fill:var(--ink); }
/* 10, NOT 9.5, AND THE REASON IS THE viewBox. This is SVG text, so what a
   reader gets is the specified size multiplied by whatever transform maps the
   viewBox onto the box it is drawn in — and .c-ring is max-width:330px, so
   the narrower the column the smaller the multiplier. At 320 the pair page
   drew this at 10.9px: under the floor, while the same rule cleared it on
   every wider screen, which is how it stayed. Raised at the source rather
   than floored in a media query, because a media query on a specified size
   still gets multiplied and would be guessing at the factor.
   It says "Sagittarius → Capricorn" on a compatibility pair, which is the
   direction — and direction is the entire argument of that section. */
.c-ring-sub { font-family:var(--font-mono); font-size:10px;
              letter-spacing:.09em; fill:var(--ink-3); }

.c-nakstrip { display:grid; grid-template-columns:repeat(9,1fr); gap:3px; }
a.c-nak { display:block; text-decoration:none; color:inherit;
          min-height:44px; }
a.c-nak em { color:var(--gold-dk); }
a.c-nak:hover { border-color:var(--gold); }
@media (max-width:1000px) { .c-nakstrip { grid-template-columns:repeat(7,1fr); } }
@media (max-width:620px) { .c-nakstrip { grid-template-columns:repeat(4,1fr); } }
.c-nak { min-width:0; padding:5px 3px 4px; border-radius:6px;
         border:1px solid var(--line-soft); background:var(--surface);
         text-align:center; overflow:hidden; }
.c-nak b { display:block; font-family:var(--font-mono); font-size:8.5px;
           color:var(--ink-3); font-weight:400; }
/* Wrap, do not truncate. "Purva Bhadrapada" needs 79px and the cell is 48
   at 360px, so nowrap+ellipsis meant nineteen of the twenty-seven read
   "Kritt…" — a strip of the whole set is worthless if you cannot read the
   set. Two lines cost 11px and name every one of them. */
.c-nak em { display:block; font-style:normal; font-size:8.5px; line-height:1.2;
            color:var(--ink-3); white-space:normal; overflow-wrap:break-word;
            min-height:2.4em; }
.c-nak-on { border-color:var(--gold); background:var(--gold-bg); }
.c-nak-on .c-nak-pads { display:flex; }
.c-nak-on b, .c-nak-on em { color:var(--gold-dk); font-weight:600; }
.c-nak-pads { display:none; gap:2px; justify-content:center; margin-top:3px;
              min-height:4px; }
.c-nak-p { width:4px; height:4px; border-radius:50%;
           border:1px solid var(--gold-lt); }
.c-nak-p.c-on { background:var(--gold); border-color:var(--gold); }

/* The label, the row of houses and the key are three separate things and
   6px between them read as one block with the caption stuck to it. */
.c-houses { display:grid; gap:12px; }
/* 3px between twelve cells is a hairline: adjacent flagged houses — 1 and 2,
   7 and 8 — looked like one wide dashed box rather than two. It stays 3 at
   360px, where twelve cells are only 21px wide and there is nothing to
   spare. */
.c-hrow { display:grid; grid-template-columns:repeat(12,1fr); gap:3px; }
@media (min-width:620px) { .c-hrow { gap:7px; } }
.c-hb { display:grid; place-items:center; height:34px; border-radius:6px;
        border:1px solid var(--line-soft); background:var(--surface);
        font-family:var(--font-mono); font-size:11px; color:var(--ink-3); }
/* flagged = the houses a rule names; on = where the graha actually is */
.c-hb-f { border-style:dashed; border-color:var(--gold-lt);
          color:var(--gold-dk); }
.c-hk { display:flex; flex-wrap:wrap; gap:7px 18px; }
.c-hk span { display:inline-flex; align-items:center; gap:7px;
             font-size:12.5px; color:var(--ink-2); }
.c-hk i { width:15px; height:15px; border-radius:4px; flex:none; }
.c-hb-on { background:var(--gold-solid); border-color:var(--gold-solid);
           color:var(--gold-solid-text); font-weight:700; }

.c-pline { display:flex; gap:3px; }
.c-pl-s { min-width:0; padding:9px 7px; border-radius:8px;
          border:1px solid var(--line); background:var(--surface);
          text-align:center; }
/* "2nd PERIOD" needs 72px and the cell was 67 even at 1280 — a label that
   ellipses at full width is a label that never fits. Wrap it. */
.c-pl-s b { display:block; font-size:12px; color:var(--ink-2);
            white-space:normal; overflow-wrap:break-word; }
.c-pl-s em { display:block; font-style:normal; font-family:var(--font-mono);
             font-size:8.5px; color:var(--ink-3); margin-top:2px; }
.c-pl-on { border-color:var(--gold); background:var(--gold-bg); }
.c-pl-on b { color:var(--gold-dk); font-weight:650; }

/* ── matrix ───────────────────────────────────────────── */
.c-mx { overflow:auto; border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); }
.c-mx table { border-collapse:collapse; font-family:var(--font-mono);
              font-size:11px; width:100%; }
.c-mx th { padding:6px 8px; color:var(--ink-3); font-weight:400;
           font-size:9.5px; letter-spacing:.06em; text-transform:uppercase;
           background:var(--surface); position:sticky; top:0; }
.c-mx tbody th { position:sticky; left:0; text-align:right; z-index:1; }
.c-mx td { padding:6px 8px; text-align:center; color:var(--ink-2);
           font-variant-numeric:tabular-nums; border:1px solid var(--surface); }
/* Four bands, not a gradient: a reader needs to tell "good" from "fine" at a
   glance, and a continuous ramp of one hue does not do that on either theme. */
.c-mx-m1 { background:var(--rail); }
.c-mx-m2 { background:var(--surface-2); }
.c-mx-m3 { background:var(--gold-bg); }
.c-mx-m4 { background:var(--gold-solid); }
.c-mx td.c-mx-m1 { color:var(--ink-3); }
.c-mx td.c-mx-m2 { color:var(--ink-2); }
.c-mx td.c-mx-m3 { color:var(--gold-dk); }
.c-mx td.c-mx-m4 { color:var(--gold-solid-text); font-weight:700; }
.c-mx-on { outline:2px solid var(--gold); outline-offset:-2px; }
.c-mx-key { display:flex; flex-wrap:wrap; gap:14px; margin-top:10px;
            font-size:12px; color:var(--ink-3); }
.c-mx-key span { display:inline-flex; align-items:center; gap:6px; }
.c-mx-key i { width:14px; height:14px; border-radius:4px;
              border:1px solid var(--line); }

/* ── day bar ──────────────────────────────────────────── */
.c-daybar { display:grid; gap:7px; }
.c-daybar { min-width:0; }
/* Sixteen choghadiya slices at 320px would be 18px each — unreadable, and they
   pushed the whole page 29px sideways. The bar scrolls inside its own box, the
   way .c-mx does; eight-part bars still fit without one. */
.c-db { display:flex; gap:2px; overflow-x:auto; scrollbar-width:thin; }
/* min-width was 38px and "Udveg" needs 36 before padding, so the quality —
   the one word the bar exists to show — ellipsed at 360px. 46 fits every
   one of the seven, and the bar already scrolls if that overflows. */
.c-db-s { min-width:46px; padding:9px 4px; border-radius:6px;
          border:1px solid var(--line-soft); background:var(--surface);
          text-align:center; }
.c-db-s b { display:block; font-size:11px; color:var(--ink-2);
            white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.c-db-s em { display:block; font-style:normal; font-family:var(--font-mono);
             font-size:8px; color:var(--ink-3); margin-top:2px;
             white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.c-db-on { background:var(--gold-solid); border-color:var(--gold-solid); }
.c-db-on b, .c-db-on em { color:var(--gold-solid-text); font-weight:650; }
.c-db-warn { background:var(--gold-bg); border-color:var(--gold-lt); }
.c-db-warn b { color:var(--gold-dk); }

/* ── loader ───────────────────────────────────────────── */
.c-ld { display:flex; align-items:center; justify-content:center; gap:11px;
        padding:34px 20px; color:var(--ink-3); font-size:var(--text-base); }
.c-ld-page { min-height:50vh; }
.c-ld-in { display:inline-flex; align-items:center; gap:8px;
           color:var(--ink-3); font-size:var(--text-sm); }
.c-ld-w { position:relative; }
/* Stale content stays readable — 55% opacity, not hidden. Someone on a slow
   connection can still read last minute's panchang while this minute's loads. */
.c-ld-stale { opacity:.55; pointer-events:none; }
.c-ld-tag { position:absolute; top:10px; left:50%; transform:translateX(-50%);
            display:flex; align-items:center; gap:8px; padding:7px 13px;
            border-radius:var(--r-pill); background:var(--surface);
            border:1px solid var(--line); font-size:var(--text-sm);
            color:var(--ink-2); box-shadow:0 4px 14px rgba(10,7,3,.12); }

/* ── autocomplete ─────────────────────────────────────── */
.c-ac { position:relative; }
.c-ac-l { position:absolute; left:0; right:0; top:100%; z-index:30;
          margin:5px 0 0; padding:5px; list-style:none; max-height:264px;
          overflow-y:auto; overscroll-behavior:contain; background:var(--surface);
          border:1px solid var(--line); border-radius:var(--r-in);
          box-shadow:0 12px 32px rgba(10,7,3,.18); }
/* Above the field instead, when the keyboard has taken everything below it —
   see place() in AUTOCOMPLETE_JS for when and why. The shadow turns over with
   it, because a list sitting above its field lit from above looks stuck to
   the wrong thing. */
.c-ac-l.c-ac-up { top:auto; bottom:100%; margin:0 0 5px;
          box-shadow:0 -12px 32px rgba(10,7,3,.18); }
.c-ac-o { display:flex; align-items:baseline; gap:8px; padding:0 11px;
          min-height:44px; border-radius:9px; cursor:pointer; }
.c-ac-o:hover, .c-ac-o[aria-selected="true"] { background:var(--gold-bg); }
.c-ac-o b { font-size:var(--text-base); font-weight:650; }
.c-ac-o span { font-size:var(--text-sm); color:var(--ink-3); flex:1;
               min-width:0; overflow:hidden; text-overflow:ellipsis;
               white-space:nowrap; }
.c-ac-o em { font-style:normal; font-family:var(--font-mono); font-size:10px;
             color:var(--ink-3); }

/* ── language ─────────────────────────────────────────── */
.c-lang { display:flex; flex:none; border:1px solid var(--line);
          border-radius:var(--r-pill); overflow:hidden; background:var(--surface); }
/* 34px in the bar, to match the brand mark beside it — measured, not guessed:
   the mark is 34x34 and these were 44, so the controls sat 10px taller than
   the logo and the row read as two different sizes of thing.
   The webapp sizes its own mark at 36 and this briefly followed it; 36 reads
   oversized in a bar this height, so the two do not match on purpose. What
   has to match is everything on THIS row.
   They go back to 44 in the drawer, where a thumb has to find them; WCAG 2.2
   AA asks 24px, and 44 is the AAA figure that belongs on touch. */
.c-lang-i { display:grid; place-items:center; min-width:38px; min-height:32px;
            padding:0 10px; font-size:12.5px; font-weight:650; color:var(--ink-3);
            text-decoration:none; }
.c-drawer-a .c-lang-i { min-width:46px; min-height:44px; padding:0 12px; }
.c-lang-i.c-on { background:var(--pill-ink); color:var(--pill-text); }
.c-lang-i:not(.c-on):hover { color:var(--gold-dk); background:var(--gold-bg); }

/* A focused radio or checkbox that lives at left:-9999px has its ring off the
   side of the screen, so the visible label it drives has to show the focus
   instead. The generators cover the tab sets and chip groups; these are
   hand-written.

   `~ *`, not `~`. The theme radios are followed by .l-app and the burger by
   <header>, so every one of these labels is nested inside a sibling rather
   than being one — the first version used the child form and lit nothing at
   all. selectable_css() emits BOTH forms and says why in its docstring; this
   is that same trap, one floor down. */
.c-radio:focus-visible ~ * .c-th-t,
.c-radio:focus-visible ~ * .c-burger,
.c-radio:focus-visible ~ * .c-dg-x { outline:2px solid var(--gold);
                                     outline-offset:2px; border-radius:8px; }

/* ── top nav ──────────────────────────────────────────── */
.c-top { position:sticky; top:0; z-index:50; background:var(--cream);
         border-bottom:1px solid var(--line); }
.c-top-r { display:flex; align-items:center; gap:10px; min-height:60px;
           max-width:var(--wrap); margin:0 auto; padding:0 16px; }
.c-brand { display:flex; align-items:center; gap:9px; margin-right:auto;
           min-height:44px;
           font-size:17px; font-weight:750; letter-spacing:-.015em;
           color:var(--ink); text-decoration:none; }
/* display and gap are set with the priority rules below, where the bar
   is actually laid out. Declaring them here as well was dead — same
   selector, same specificity, and the later one wins. */
.c-nav { margin-right:auto; }
.c-nav-i { padding:0 10px; min-height:44px; display:grid; place-items:center;
           font-size:14px; font-weight:600; color:var(--ink-2); border-radius:9px;
           text-decoration:none; white-space:nowrap; }
.c-nav-i:hover { background:var(--surface-2); color:var(--ink); }
.c-nav-i.c-on { color:var(--gold-dk); background:var(--gold-bg); }

/* ── nav groups ───────────────────────────────────────── */
/* The panel opens on hover AND on focus-within: hover is the pointer, and
   focus-within is what makes the tab key work without a line of script. */
.c-nav-g { position:relative; display:flex; }
.c-nav-t { gap:5px; grid-auto-flow:column; }
/* "Soon" beside a nav item that is listed but not ready. --mid, the warning
   pair, rather than --gold: gold is what every live control on this site is
   painted in, and a badge in it reads as a promotion rather than a caveat. */
/* THE BADGE MUST NOT MAKE ITS ROW TALLER. checks/nav-check.mjs holds every
   row in a panel to one height — "a wrapped blurb makes its row taller than
   its neighbours and the list stops reading as a set of peers" — and a badge
   with padding and a border does the same thing to the label line. Fixed
   height, line-height to match, and no vertical-align nudge, so its box is
   shorter than the label's line box and contributes nothing to it. */
.c-nav-soon { display:inline-block; margin-left:7px; font-style:normal;
              font-size:11px; font-weight:700; letter-spacing:.08em;
              text-transform:uppercase; padding:0 5px; border-radius:4px;
              height:16px; line-height:15px; vertical-align:middle;
              background:var(--mid-bg); color:var(--mid);
              border:1px solid var(--mid); }
.c-nav-ch { width:15px; height:15px; color:var(--ink-3);
            transition:transform .16s ease; }
.c-nav-t:hover .c-nav-ch { color:inherit; }
.c-nav-p { position:absolute; top:100%; left:0; z-index:60;
           /* The bridge. Without it the pointer crosses a 1px gap between the
              trigger and the card, hover drops, and the menu shuts under it. */
           /* Wide enough that no blurb wraps. At 296 two of the four Free
              Services blurbs took a second line and the rows in one panel came
              out 55px and 74px, which reads as a list of unequal things. */
           padding-top:7px; min-width:334px; display:none;
           }
.c-nav-pw { display:grid; gap:2px; padding:7px;
            background:var(--surface); border:1px solid var(--line);
            border-radius:14px; box-shadow:0 14px 34px -12px rgba(0,0,0,.28),
                                          0 2px 6px rgba(0,0,0,.06); }
.c-nav-g:hover .c-nav-p, .c-nav-g:focus-within .c-nav-p { display:block; }
.c-nav-g:hover .c-nav-ch, .c-nav-g:focus-within .c-nav-ch {
    transform:rotate(180deg); }
.c-nav-pi, .c-dg-i { display:flex; align-items:flex-start; gap:11px;
                     padding:9px 10px; border-radius:10px;
                     text-decoration:none; color:var(--ink); }
.c-nav-pi:hover, .c-dg-i:hover { background:var(--surface-2); }
.c-nav-pi.c-on, .c-dg-i.c-on { background:var(--gold-bg); }
.c-nav-pk { flex:none; display:grid; place-items:center; width:32px; height:32px;
            border-radius:9px; background:var(--gold-bg); color:var(--gold-dk); }
.c-nav-pi.c-on .c-nav-pk, .c-dg-i.c-on .c-nav-pk {
    background:var(--gold-solid); color:var(--gold-solid-text); }
.c-nav-px { min-width:0; }
.c-nav-px b { display:block; font-size:14px; font-weight:650; line-height:1.35; }
.c-nav-px i { display:block; font-style:normal; font-size:12.5px; line-height:1.4;
              color:var(--ink-3); margin-top:1px; }
@media (prefers-reduced-motion:reduce) {
  .c-nav-p, .c-nav-ch { transition:none; }
}

/* The directory panel. Drops from the trigger's left edge like the other
   three — an earlier version anchored it right to dodge an overflow, which
   made it open to the LEFT of the word you pressed and read as belonging to
   Horoscope. The width answers the overflow instead: at the 1060 breakpoint
   the Calculators trigger starts at 541, leaving 503px to its right, so 500 is
   the ceiling. Every label still fits — the widest, "Name Numerology", is
   about 118px in a 133px column. */
.c-nav-gw .c-nav-p { width:min(500px, calc(100vw - 32px)); min-width:0; }
.c-nav-mg { display:grid; grid-template-columns:repeat(3,1fr); gap:2px 6px; }
.c-nav-mc { display:flex; flex-direction:column; min-width:0; }
.c-nav-mh { font-family:var(--font-mono); font-size:9.5px; letter-spacing:.12em;
            text-transform:uppercase; color:var(--gold-dk);
            padding:8px 10px 5px; }
/* 44, not the 36 this had first. The argument for 36 was that a hover panel
   is clicked with a mouse — but a touch laptop opens it too, and an invariant
   that carves out an exception for the menu I just designed is not one. The
   panel grew 48px. */
.c-nav-mi { display:flex; align-items:center; min-height:44px; padding:0 10px;
            border-radius:8px; font-size:13.5px; color:var(--ink-2);
            text-decoration:none; white-space:nowrap; overflow:hidden;
            text-overflow:ellipsis; }
.c-nav-mi:hover { background:var(--surface-2); color:var(--ink); }
.c-nav-mi.c-on { background:var(--gold-bg); color:var(--gold-dk); }
.c-nav-mm { display:flex; align-items:center; justify-content:center; gap:7px;
            min-height:44px; margin-top:7px; padding-top:9px;
            border-top:1px solid var(--line-soft); font-size:13.5px;
            font-weight:650; color:var(--gold-dk); text-decoration:none; }
.c-nav-mm:hover { background:var(--gold-bg); border-radius:9px; }
/* In the drawer there is one column. */
.c-dg-p .c-nav-mg { grid-template-columns:1fr; gap:0; }
.c-dg-p .c-nav-mh { padding-left:0; }
.c-dg-p .c-nav-mm { justify-content:flex-start; }

/* The drawer's accordion. The group's own page stays a link; only the chevron
   toggles, so "Horoscope" is still reachable on a phone. */
.c-dg-h { display:flex; align-items:center; }
.c-dg-h .c-nav-i { flex:1 1 auto; justify-items:start; justify-content:start; }
.c-dg-x { flex:none; display:grid; place-items:center; width:44px; height:44px;
          cursor:pointer; border-radius:10px; color:var(--ink-3); }
.c-dg-x:hover { background:var(--surface-2); color:var(--ink); }
.c-dg-p { display:none; padding:2px 0 8px 10px; margin-left:9px;
          border-left:1px solid var(--line-soft); }
.c-dg > input:checked ~ .c-dg-p { display:grid; gap:2px; }
.c-dg > input:checked ~ .c-dg-h .c-nav-ch { transform:rotate(180deg); }
.c-dg > input:focus-visible ~ .c-dg-h .c-dg-x {
    outline:2px solid var(--gold); outline-offset:-3px; }

/* Hidden until there is room. At 360px the brand, language switch, theme
   icon, sign-in button and burger came to 422px in a 328px row. */
.c-top-a { display:none; align-items:center; gap:8px; }
/* The header-only slot. No breakpoint of its own — that is the whole point of
   it, and it is why the theme toggle can leave the drawer without vanishing
   on a phone. */
.c-top-b { display:flex; align-items:center; gap:8px; margin-left:auto; }
.c-top-a + .c-top-b { margin-left:0; }
/* display:grid lives HERE, not in the layout's stylesheet. It was declared
   there, unconditionally and later in the cascade, which silently beat the
   min-width:560px rule below — same specificity, and a media query does not
   add any. The drawer went on showing its actions at every width and the fix
   looked like it had worked. */
.c-drawer-a { display:grid; gap:12px; padding:14px 10px 2px; margin-top:8px;
              border-top:1px solid var(--line-soft); }
.c-burger { display:grid; place-items:center; width:44px; height:44px; flex:none;
            cursor:pointer; border:1px solid var(--line); border-radius:10px;
            color:var(--ink-2); background:var(--surface); }
/* A side panel: full height, part of the width. It was the other way round —
   edge to edge horizontally and only as tall as its contents — which reads as
   a dropdown stuck under the header rather than a menu, and on a long list
   pushed the page down instead of scrolling itself. */
.c-drawer { position:fixed; top:0; right:0; bottom:0; z-index:70;
            width:min(340px, 86vw); display:flex; flex-direction:column;
            background:var(--surface); border-left:1px solid var(--line);
            box-shadow:-18px 0 44px -20px rgba(0,0,0,.34);
            transform:translateX(101%); transition:transform .22s ease;
            visibility:hidden; }
.c-drawer-h { display:flex; align-items:center; justify-content:space-between;
              gap:12px; padding:14px 10px 12px 16px; flex:none;
              border-bottom:1px solid var(--line-soft); }
/* 11px, not 10. This label and the Soon badge below were the two smallest
   strings on any page on the site, and they sit in the drawer — which only
   exists on a phone, so there is no width where the small size was earning
   anything. */
.c-drawer-h span { font-family:var(--font-mono); font-size:11px;
              letter-spacing:.13em; text-transform:uppercase;
              color:var(--ink-3); }
.c-drawer-x { display:grid; place-items:center; width:44px; height:44px;
              flex:none; cursor:pointer; border-radius:10px; color:var(--ink-2);
              border:1px solid var(--line); background:var(--surface); }
.c-drawer-x:hover { color:var(--gold-dk); border-color:var(--gold-lt); }
/* The panel scrolls, not the page behind it. */
.c-drawer-s { flex:1; min-height:0; overflow-y:auto; overscroll-behavior:contain;
              padding:6px 10px 20px; display:flex; flex-direction:column; }
/* Tapping outside closes it — a label for the same checkbox, so still no
   script. */
.c-scrim { position:fixed; inset:0; z-index:69; background:rgba(20,14,6,.44);
           opacity:0; visibility:hidden; transition:opacity .22s ease;
           cursor:pointer; }
.c-drawer .c-nav-i { justify-items:start; justify-content:start;
                     min-height:48px; padding:0 10px; }
#nav:checked ~ .c-top .c-drawer, #nav:checked ~ * .c-top .c-drawer {
    transform:none; visibility:visible; }
#nav:checked ~ .c-top .c-scrim, #nav:checked ~ * .c-top .c-scrim {
    opacity:1; visibility:visible; }
@media (prefers-reduced-motion:reduce) {
  .c-drawer, .c-scrim { transition:none; }
}
/* Progressive, not all-or-nothing.

   Measured on the real header at 1440px: brand 130, actions 228, padding 32,
   and the six items 138 / 136 / 118 / 121 / 91 / 76. That makes the row need

     0 items + burger   466        3 items + burger    864
     1 item  + burger   606        4 items + burger    987
     2 items + burger   744        6 items, no burger 1104

   The single 1060px breakpoint was wrong at both ends. Above it, all six were
   shown in a row that needs 1104 — 44px of overflow for 44px of viewport.
   Below it, every item went behind the burger even at 1050, where four fit
   with room to spare, which is the bug as reported: a nearly empty bar with a
   hamburger next to it.

   Each item carries its priority as .c-np<n> inline and .c-nd<n> in the
   drawer, and the two are complements — an item shown in the bar is hidden in
   the drawer at the same width, so nothing is ever offered twice. */
.c-nav { display:flex; gap:0; }
.c-nav > * { display:none; }
.c-top-a { display:none; }
.c-burger { display:grid; }

@media (min-width:670px) {
  .c-nav .c-np1 { display:flex; }
  .c-drawer .c-nd1 { display:none; }
}
@media (min-width:800px) {
  .c-nav .c-np1, .c-nav .c-np2 { display:flex; }
  .c-drawer .c-nd1, .c-drawer .c-nd2 { display:none; }
}
@media (min-width:915px) {
  .c-nav .c-np1, .c-nav .c-np2, .c-nav .c-np3 { display:flex; }
  .c-drawer .c-nd1, .c-drawer .c-nd2, .c-drawer .c-nd3 { display:none; }
}
@media (min-width:1030px) {
  .c-nav .c-np1, .c-nav .c-np2, .c-nav .c-np3, .c-nav .c-np4 { display:flex; }
  .c-drawer .c-nd1, .c-drawer .c-nd2, .c-drawer .c-nd3, .c-drawer .c-nd4 { display:none; }
}
@media (min-width:1115px) {
  .c-nav .c-np1, .c-nav .c-np2, .c-nav .c-np3, .c-nav .c-np4, .c-nav .c-np5 { display:flex; }
  .c-drawer .c-nd1, .c-drawer .c-nd2, .c-drawer .c-nd3, .c-drawer .c-nd4, .c-drawer .c-nd5 { display:none; }
}
@media (min-width:1140px) {
  .c-nav .c-np1, .c-nav .c-np2, .c-nav .c-np3, .c-nav .c-np4, .c-nav .c-np5, .c-nav .c-np6 { display:flex; }
  .c-drawer .c-nd1, .c-drawer .c-nd2, .c-drawer .c-nd3, .c-drawer .c-nd4, .c-drawer .c-nd5, .c-drawer .c-nd6 { display:none; }
}

/* The actions fit as soon as anything else does — and the moment they are in
   the bar they come out of the drawer, which is the same complement rule the
   nav items follow above. Without this the drawer went on offering Sign in and
   Sign up from 560px all the way to 1140px while the bar showed them too. */
@media (min-width:560px) {
  .c-top-a { display:flex; }
  .c-drawer-a { display:none; }
  .c-brand { margin-right:16px; }
}
@media (min-width:1140px) { .c-brand { margin-right:14px; } }

/* Only once every item is in the bar does the burger go — and with it the
   drawer, which by then would be empty apart from the actions. */
@media (min-width:1140px) {
  .c-burger { display:none; }
  .c-drawer, .c-scrim,
  #nav:checked ~ .c-top .c-drawer, #nav:checked ~ .c-top .c-scrim {
      display:none; }
}

/* ── hero ─────────────────────────────────────────────── */
.c-hero { padding:26px 0 30px;
          background:
            radial-gradient(120% 90% at 82% 0%, var(--gold-bg) 0%, transparent 62%),
            var(--cream); }
/* The centred variant's own padding. It has to be declared HERE — after
   .c-hero, whose specificity it matches, and before the min-width:1000px
   block that overrides it. Written below that block instead, a bare rule
   wins at every width including 1000+, which is how this briefly shipped as
   16px on a 1280px screen while the desktop rule said 26. dupe_rule.py did
   not catch it: it compares declarations at the same width, and these are
   at two. */
.c-hero-mid { padding:16px 0 4px; }
.c-hero-r { max-width:var(--wrap); margin:0 auto; padding:0 16px;
            display:grid; gap:22px; }
.c-hero-e { display:inline-block; font-family:var(--font-mono); font-size:10px;
            letter-spacing:.14em; text-transform:uppercase; color:var(--gold-dk);
            padding:5px 10px; border-radius:var(--r-pill);
            background:var(--gold-bg); margin-bottom:14px; }
.c-hero-t { font-family:var(--font-serif); font-size:clamp(30px,7.4vw,50px);
            line-height:1.06; letter-spacing:-.022em; font-weight:600;
            margin:0 0 14px; text-wrap:balance; }
.c-hero-t em { font-style:normal; color:var(--gold-dk); }
.c-hero-l { font-size:var(--text-md); line-height:1.62; color:var(--ink-2);
            margin:0; max-width:56ch; }
.c-hero-c { display:flex; flex-wrap:wrap; gap:6px; }
.c-hero-b { display:flex; flex-direction:column; gap:16px; }
/* 1000, not 940. Between them the two columns are each about 430px, the
   headline and both buttons wrap, and the text column outruns the orbit by
   107px. A stacked hero at that width is simply better than a cramped one. */
@media (min-width:1000px) {
  .c-hero { padding:52px 0 58px; }
  /* auto 1fr, not auto auto. The form spans both rows, and with two auto rows
     the browser shares its surplus height between them, which stretched row 1
     past the end of the lede. Row 1 is exactly the text; row 2 is everything
     under it, and it stretches to the bottom of the card so the two columns
     finish together instead of leaving a hole. */
  .c-hero-r { grid-template-columns:1.05fr .95fr; gap:22px 44px; align-items:start;
              grid-template-rows:auto 1fr; }
  .c-hero-x { grid-area:1 / 1; }
  /* Between the two-column breakpoint and 1200 the headline is still at its
     full clamp size but the column it sits in has halved, so it wraps to a
     third line and the left column outruns the card by up to 133px. The size
     has to answer to the COLUMN, not the viewport. */
  @media (max-width:1199px) {
    .c-hero-t { font-size:clamp(30px,4vw,40px); }
    /* The lede loses a line at this width for the same reason the headline
       did, and the column is narrow enough that 14px still reads comfortably. */
    .c-hero-l { font-size:var(--text-base); }
  }
  /* Stack it. Making the column's bottom line up with the card's meant
     space-between, which just converted the hole into distributed slack — the
     same hole, spread out. Content that follows content, and whatever is left
     is padding under the column rather than a gap through the middle of it. */
  .c-hero-b { grid-area:2 / 1; justify-content:flex-start; }
  .c-hero-a { grid-area:1 / 2 / span 2; }
  /* The centred variant. One row, one item in each column, and the text
     column centred against whatever the aside's height turns out to be.
     SHORTER BY SEVENTY, TAKEN FROM ITS OWN PADDING rather than from the
     orbit, which is the thing setting the height and the reason to come to
     the page. 52 above and 58 below is right when the text starts at the top
     and the eye has to travel to it; once the whole column is centred in its
     own height that padding is being counted twice, and the headline sits a
     long way down a first screen. 52+58 to 26+34 took fifty off; 16+24 takes
     another twenty. Below that the eyebrow starts crowding the nav above. */
  /* 8 below, not 24. What follows this hero is the question strip, and the
     gap between the orbit and the first pill was 74px — 40 of it this
     padding plus the strip label's. A band that belongs to the hero should
     sit under it, not across a gutter from it. 74 to 44 to 32. */
  .c-hero-mid { padding:16px 0 0; }
  .c-hero-mid .c-hero-r { grid-template-rows:auto; align-items:center; }
  .c-hero-mid .c-hero-s { grid-area:1 / 1; display:flex; flex-direction:column;
                          gap:22px; min-width:0; }
  .c-hero-mid .c-hero-a { grid-area:1 / 2; }
}
/* Below the two-column breakpoint the stack is the page order and there is
   nothing to centre against. */
.c-hero-mid .c-hero-s { display:contents; }
@media (min-width:1000px) { .c-hero-mid .c-hero-s { display:flex; } }
/* ── ON A PHONE THE BUTTONS FOLLOW THE ORBIT ──
   Stacked, the source order put both CTAs between the lede and the ring of
   astrologers, so a reader met "Talk to an astrologer" before being shown a
   single one. The ring is the argument for pressing the button; a button
   above its own reason is a button pressed by fewer people.

   Only `order` is needed, and only because .c-hero-s is already
   display:contents below 1000px — .c-hero-x, .c-hero-b and .c-hero-a are
   siblings in the same grid, so this is three integers rather than a second
   copy of the markup. Above 1000px .c-hero-s becomes a flex box again, the
   two columns come back, and none of this applies. */
@media (max-width:640px) {
  .c-hero-mid .c-hero-x { order:1; }
  .c-hero-mid .c-hero-a { order:2; }
  .c-hero-mid .c-hero-b { order:3; }
}

/* ── footer ───────────────────────────────────────────── */
.c-foot { position:relative; border-top:1px solid var(--line);
          background:var(--surface-2); margin-top:8px; overflow:hidden; }
/* NO GOLD BAR ACROSS THE TOP. The deployed footer had one — 600px of gradient
   centred on the band — and it was carried over with the rest of the
   structure. On the page it reads as a glow bleeding out of the seam rather
   than as a rule: it is the only gradient on the whole site that is not
   attached to something, it lands directly under the last section's own
   border, and the two together make a doubled edge.
   The border-top above is the separation the band needs. */
.c-foot-r { max-width:var(--wrap); margin:0 auto; padding:38px 16px 0; }
/* The bottom bar's own 18px was the band's lower padding. A footer built
   without one (see the guard in footer()) ended flush against the viewport
   edge, so the link block supplies it instead when it is the last thing. */
.c-foot-g:last-child { padding-bottom:34px; }
/* Two columns on a phone, not one. Stacked, the three lists ran 1,100px and
   the copyright was a thousand pixels below the last link anyone would read.
   Two fits every label on one line down to 320px. */
.c-foot-g { display:grid; gap:22px 22px; grid-template-columns:repeat(2,1fr); }
/* The brand column takes a third and the three link columns share the rest —
   the deployed proportions (4 / 2.5 / 2.5 / 3 of twelve). Below 900 the three
   sit in a row under the brand, which is what a phone wants; below 560 they
   stack, because three columns of link text at 320px is four words a line. */
@media (min-width:560px) {
  .c-foot-g { grid-template-columns:repeat(3,1fr); gap:26px 24px; } }
/* FOUR LINK COLUMNS NOW, so the old `1.55fr 1fr 1fr 1.1fr` is one track
   short. Between 900 and 1119 the four sit in a row of their own and the
   brand keeps the full width above them — five tracks at 900px gives each
   column about 170px, and "Talk to an Astrologer" is two lines at that
   width. Above 1120 there is room for the brand to take its own track
   beside them, which is the deployed proportion. */
@media (min-width:900px) {
  .c-foot-g { grid-template-columns:repeat(4,1fr); gap:30px 30px; }
}
@media (min-width:1120px) {
  .c-foot-g { grid-template-columns:1.5fr 1fr 1fr 1fr 1fr; }
}
.c-foot-x { grid-column:1 / -1; }
@media (min-width:1120px) { .c-foot-x { grid-column:auto; } }
.c-foot-w { font-size:23px; font-weight:800; letter-spacing:-.02em;
            color:var(--ink); margin-bottom:11px; }
/* 23px is a desktop size sitting in a 358px-wide column, where it is the
   largest type in the footer by 10px and reads as a heading for the links
   under it rather than as a sign-off. 19px still carries the wordmark. */
@media (max-width:559px) { .c-foot-w { font-size:19px; margin-bottom:9px; } }
.c-foot-w em { font-style:normal; color:var(--gold-dk); }
.c-foot-p { font-size:13px; line-height:1.72; color:var(--ink-3); margin:0;
            max-width:44ch; }
.c-foot-c { display:flex; flex-direction:column; min-width:0; }
.c-foot-h { display:block; font-family:var(--font-mono); font-size:10px;
            letter-spacing:.13em; text-transform:uppercase; color:var(--gold-dk);
            margin-bottom:6px; }
/* 44px, not 28. A footer is where a thumb goes hunting for Privacy or
   Support, usually one-handed, usually on a bus. */
.c-foot-c a { display:flex; align-items:center; gap:6px; min-height:44px;
              font-size:13.5px; color:var(--ink-2); text-decoration:none; }
.c-foot-c a:hover { color:var(--gold-dk); }
.c-foot-c a svg { color:var(--ink-3); }
.c-foot-b { display:flex; flex-wrap:wrap; align-items:center;
            justify-content:space-between; gap:10px 18px; margin-top:12px;
            padding:14px 0 18px; border-top:1px solid var(--line-soft);
            font-size:12px; color:var(--ink-3); }
.c-foot-soc { display:flex; align-items:center; gap:2px; flex-wrap:wrap;
              order:-1; width:100%; }
@media (min-width:720px) { .c-foot-soc { order:0; width:auto; } }
.c-foot-s { display:grid; place-items:center; width:44px; height:44px;
            border-radius:10px; color:var(--ink-3);
            transition:transform .18s ease, color .18s ease; }
.c-foot-s:hover { color:var(--gold-dk); transform:translateY(-2px); }
.c-foot-m { display:inline-flex; align-items:center; gap:6px; min-height:44px;
            margin-left:6px; font-size:13px; color:var(--ink-2);
            text-decoration:none; }
.c-foot-m:hover { color:var(--gold-dk); }
@media (prefers-reduced-motion:reduce) { .c-foot-s { transition:none; } }

/* ── popover field ────────────────────────────────────── */
.c-pf { position:relative; margin-bottom:var(--space-3); }
.c-pf-l { display:block; font-family:var(--font-mono); font-size:10px;
          letter-spacing:.1em; text-transform:uppercase; color:var(--ink-3);
          margin-bottom:5px; }
.c-pf-t { display:flex; align-items:center; gap:10px; width:100%; min-height:46px;
          padding:0 13px; cursor:pointer; border:1px solid var(--line);
          border-radius:10px; background:var(--surface); }
.c-pf-t b { flex:1; min-width:0; font-size:var(--text-md); font-weight:600; }
.c-pf-t svg { color:var(--ink-3); }
.c-pf-t:hover { border-color:var(--gold-lt); }
.c-pf-t:focus-visible { outline:2px solid var(--gold); outline-offset:1px; }
.c-pf-h { display:block; font-size:var(--text-sm); color:var(--ink-3);
          margin-top:5px; line-height:1.45; }
.c-pf-p { display:none; }
input[type="checkbox"].c-radio:checked ~ .c-pf-p { display:block; }
/* Phone: a sheet from the bottom. A dropdown here sits under your thumb. */
.c-pf-bg { position:fixed; inset:0; z-index:90; background:rgba(0,0,0,.45); }
.c-pf-c { position:fixed; left:0; right:0; bottom:0; z-index:91; max-height:88vh;
          overflow:auto; padding:16px 14px calc(14px + env(safe-area-inset-bottom));
          background:var(--surface); border-top:1px solid var(--line);
          border-radius:20px 20px 0 0; box-shadow:0 -14px 40px rgba(0,0,0,.22); }
.c-pf-done { margin-top:12px; width:100%; }
@media (min-width:640px) {
  .c-pf-bg { position:fixed; background:transparent; }
  .c-pf-right .c-pf-c { left:auto; right:0; }
  .c-pf-c { position:absolute; left:0; right:auto; top:calc(100% + 6px); bottom:auto;
            z-index:91; width:max-content; min-width:min(322px,92vw);
            max-width:min(400px,92vw); max-height:none;
            border:1px solid var(--line); border-radius:var(--r-card);
            box-shadow:0 18px 44px rgba(0,0,0,.16); padding:14px; }
  .c-pf-done { display:none; }
}

/* ── time picker ──────────────────────────────────────── */
.c-tp-out { display:flex; align-items:baseline; justify-content:center; gap:1px;
            font-family:var(--font-serif); font-size:30px; font-weight:600;
            padding:4px 0 12px; margin-bottom:10px; letter-spacing:-.01em;
            border-bottom:1px solid var(--line-soft); }
.c-tp-out b { font-weight:600; }
.c-tp-out span { opacity:.45; }
/* WHAT AN UNANSWERED READOUT SAYS. Nothing is pre-selected — a defaulted
   meridiem is a twelve-hour error — so with no hour and no minute chosen the
   :checked rules write nothing and the readout was a bare colon floating in
   white space. It read as a broken picker rather than an empty one. Dashes
   say the same thing and say it on purpose; the generated `:checked` rules
   are (1,1,0) against this (0,1,0) and paint over them the moment you choose.
   Every other page hands this component a value, so this shows only where
   there genuinely is not one. */
.c-tp-oh::after, .c-tp-ot::after { content:'--'; opacity:.35; }
.c-pf-face .c-tp-oh::after, .c-pf-face .c-tp-ot::after { content:''; }
.c-tp-om { font-size:16px; margin-left:7px; color:var(--ink-3); }
.c-tp-lb { display:block; font-family:var(--font-mono); font-size:9.5px;
           letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3);
           margin:10px 0 6px; }
.c-tp-g { display:grid; gap:5px; margin-bottom:5px; }
.c-tp-g6 { grid-template-columns:repeat(6,1fr); }
.c-tp-g5 { grid-template-columns:repeat(5,1fr); }
/* Twelve rows of five exact minutes, one shown at a time — whichever block
   the chosen minute falls in. Hidden by default so a browser that somehow
   resolves none of the sixty :checked rules shows an empty row rather than
   twelve stacked ones. */
.c-ck-fr { display:none; }
.c-tp-g2 { grid-template-columns:repeat(2,1fr); }
.c-tp-mer { margin-top:12px; }
.c-tp-c { display:grid; place-items:center; min-height:44px; cursor:pointer;
          font-size:13.5px; font-weight:600; color:var(--ink-2);
          border:1px solid var(--line); border-radius:9px;
          background:var(--surface); }
.c-tp-c:hover { border-color:var(--gold); background:var(--gold-bg);
                color:var(--gold-dk); }

/* ── the clock face ───────────────────────────────────── */
.c-ck { display:grid; place-items:center; padding:6px 0 2px; }
.c-ck-face { position:relative; width:320px; height:320px; border-radius:50%;
             border:1px solid var(--line); background:var(--surface-2); }
.c-ck-pin { position:absolute; left:50%; top:50%; width:7px; height:7px;
            margin:-3.5px 0 0 -3.5px; border-radius:50%;
            background:var(--gold-dk); }
/* rotate out to the ring, then counter-rotate so the digits stay upright */
/* 44px, which is tokens.TAP_MIN. These were 40 and the minute marks 34 —
   both under the floor the whole product is held to, and shell-check never
   caught it because it cannot measure inside a closed popover. Twelve marks
   at 44px need 528px of circumference, so the minute ring moved out to r=88
   and the hour ring to r=134 to clear it; the face grew from 264 to 320 to
   hold both. */
.c-ck-h, .c-ck-m { position:absolute; left:50%; top:50%; display:grid;
                   place-items:center; width:44px; height:44px;
                   margin:-22px 0 0 -22px; border-radius:50%; cursor:pointer;
                   transform:rotate(var(--a)) translateY(calc(-1 * var(--r)))
                             rotate(calc(-1 * var(--a))); }
.c-ck-h { font-family:var(--font-serif); font-size:17px; font-weight:600;
          color:var(--ink); }
/* Size and margin are NOT repeated here. They were — width:34px, after the
   shared 44px rule twenty lines above, so the shared rule was dead for the
   minute marks and they measured 34 against a 44 floor. checks/dupe_rule.py
   cannot catch this: `.c-ck-h, .c-ck-m` and `.c-ck-m` are different
   selectors, so it is not a file arguing with itself by that test. */
.c-ck-m { font-family:var(--font-mono); font-size:12px; color:var(--ink-3); }
.c-ck-h:hover, .c-ck-m:hover { background:var(--gold-bg);
                               color:var(--gold-dk); }
.c-ck-f { font-size:12.5px; }
@media (max-width:380px) {
  /* The radii do NOT shrink here any more. Pulling the minute ring in to 84px
     puts its twelve marks 42px apart, and a 44px target in a 42px slot
     overlaps its neighbour — which is worse than a small target, not better.
     The face is the same 320 as everywhere else; it fits a 360px phone
     inside the sheet's padding, measured. */
  .c-ck-face { width:320px; height:320px; }
}
/* Under 340px the sheet gives the face 292px and a 320px circle runs off the
   edge, so the radii come in with it. That leaves 41px between minute marks,
   and the marks have to come down to 40 to match: a 44px target in a 41px
   slot overlaps its neighbour, which is a mis-tap, while a 40px target is
   just a slightly smaller one. Four under the floor, on the narrowest phones
   only, and measured rather than assumed. */
@media (max-width:340px) {
  .c-ck-face { width:288px; height:288px; }
  .c-ck-h { --r:120px !important; }
  .c-ck-m { --r:79px !important; width:40px; height:40px;
            margin:-20px 0 0 -20px; }
}


/* ── agent orbit ──────────────────────────────────────── */
.c-orb { position:relative; width:100%; max-width:520px; aspect-ratio:1;
         margin:0 auto; display:grid; place-items:center; overflow:hidden;
         container-type:size; container-name:orb; }
.c-orb-ring { position:absolute; inset:0; }
/* Each card rides a circle. offset-rotate:0deg means it never turns — no
   counter-rotation to keep in step, so nothing can tilt. offset-distance
   carries it round, starting from its own --s. */
/* --r IS THE RADIUS, AND IT IS A VARIABLE BECAUSE THE ANIMATION OWNS IT.
   offset-path is animated, so an animation keyframe beats any static value a
   container query sets — a query that "changed the radius" by declaring
   offset-path did nothing at all, and the c-orb-travel-sm keyframes written to
   work around that were never wired to an animation-name and had been dead
   since the day they were added. One variable, read by both the resting value
   and every keyframe, so there is one place to change and nothing to keep in
   step. */
.c-orb-c { position:absolute; inset:0; margin:auto; width:18.5cqw; height:max-content;
           --r:36.5%;
           offset-path:circle(var(--r) at 50% 50%); offset-rotate:0deg;
           offset-distance:calc(var(--s) * 100%);
           padding:1.7cqw 1.2cqw 1.4cqw; text-align:center; border-radius:2.2cqw;
           background:var(--surface); border:1px solid var(--line);
           box-shadow:0 6px 22px rgba(0,0,0,.10);
           animation:c-orb-travel 26s linear infinite; }
.c-orb-ph { width:6.9cqw; height:6.9cqw; border-radius:50%; object-fit:cover;
            display:block; margin:0 auto 1.1cqw; background:var(--surface-2);
            outline:2px solid var(--gold-bg); }
.c-orb-c .c-avatar { width:6.9cqw; height:6.9cqw; border-radius:50%;
            font-size:3cqw; margin:0 auto 1.1cqw; display:grid; }
/* Full names wrap where they need to — "Acharya Aryabhata" does not fit 96px
   on one line at any readable size. No reserved second line: a card is as tall
   as its own name, so "Astro Kuber" does not carry an empty row under it. The
   ring clears at the TALLEST card, which is what the geometry is checked
   against; the shorter ones simply have more room. */
.c-orb-c b { display:block; font-size:clamp(8.5px,2.05cqw,11.5px); font-weight:700;
             line-height:1.22; }
/* The visible gap was mostly leading, not margin: the name's 1.22 line-height
   and the tag's default line-height each add half a line ofempty space above and
   below the glyphs. Both are trimmed here, so the 2px that remains is the
   actual gap rather than 2px plus four of leading. */
.c-orb-on { display:inline-flex; align-items:center; gap:4px; margin-top:.4cqw;
            line-height:1; font-style:normal; font-size:clamp(7px,1.6cqw,9px);
            font-weight:700; color:var(--good); }
.c-orb-on::before { content:''; width:5px; height:5px; border-radius:50%;
            background:var(--good); animation:c-orb-pulse 2s ease-in-out infinite; }
.c-orb-mid { position:relative; z-index:2; width:27.5cqw; padding:2.4cqw 2cqw 2.1cqw;
           border-radius:2.7cqw; background:var(--surface);
           border:1px solid var(--gold-lt);
           box-shadow:0 12px 38px rgba(0,0,0,.13); text-align:center; }
.c-orb-ph-lg { width:9cqw; height:9cqw; margin-bottom:1.4cqw;
               outline:3px solid var(--gold-bg); }
.c-orb-mid .c-avatar { width:9cqw; height:9cqw; font-size:3.6cqw;
               margin:0 auto 1.4cqw; display:grid; }
.c-orb-mid b { display:block; font-size:clamp(9.5px,2.5cqw,13.5px); font-weight:750;
               line-height:1.25; }
.c-orb-mid i { display:block; font-style:normal; font-size:clamp(8px,1.95cqw,10.5px);
               line-height:1.25; color:var(--ink-3); margin-top:1px;
               white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* The centre card kept the old spacing: its own override set margin-top
   before the ring rule was tightened, so "Advanced Vedic Jyotish" and
   "Answering now" still sat a full line apart. */
.c-orb-mid .c-orb-on { font-size:clamp(7.5px,1.8cqw,10px); margin-top:.5cqw; }

/* The four phases home-hero.js documents. The collapse rides the same single
   animation: offset-path scales to nothing at the centre and back out. */
@keyframes c-orb-travel {
  0%   { offset-path:circle(var(--r) at 50% 50%);
         offset-distance:calc(var(--s) * 100%); }
  8%   { offset-path:circle(0% at 50% 50%);
         offset-distance:calc(var(--s) * 100%); }
  12%  { offset-path:circle(0% at 50% 50%);
         offset-distance:calc(var(--s) * 100%); }
  20%  { offset-path:circle(var(--r) at 50% 50%);
         offset-distance:calc(var(--s) * 100%); }
  100% { offset-path:circle(var(--r) at 50% 50%);
         offset-distance:calc(var(--s) * 100% + 100%); }
}
@keyframes c-orb-pulse { 0%,100% { opacity:1; } 50% { opacity:.3; } }
/* ── ONE STATUS LINE ON A PHONE, NOT TWO ──
   The hero eyebrow says "Answering now" with a pulsing dot, and the orbit's
   centre card says it again a thumb below. On a wide screen they are a
   scroll apart and read as two facts; stacked they are a stutter. The
   eyebrow speaks for the platform, so the card's copy is the one that goes.
   The satellite cards keep theirs — those say something the eyebrow does
   not, which is that THIS astrologer is free. */
@media (max-width:640px) {
  .c-orb-mid .c-orb-on { display:none; }
}
/* ── THE ORBIT KEEPS ITS OWN PROPORTIONS ON A PHONE ──
   It was resized here once and put back. The centre card at 27.5cqw does
   truncate a long specialty at 390px — "Advanced Vedic Jyotish" becomes
   "Advanced Vedic Jy…" — and growing it to fix that is what broke the
   cluster: at 50cqw the card is wide enough that the faces on the diagonals
   sit underneath it rather than around it. The clearance arithmetic that
   said otherwise only checked the cardinal points, and a rectangle's corners
   reach further than its edges.
   The ring reads as a ring at these numbers and that is what this element is
   for. If the truncation is worth fixing it wants a shorter specialty or a
   two-line card, not a wider one.

   THE TWO STRAY BRACES THAT USED TO SIT HERE ATE THE NEXT RULE. They were the
   leftovers of a @media block deleted when the orbit was put back, and a
   stray } is not simply ignored: the first is discarded, and the second is
   read as the start of a qualified rule whose prelude runs on until the next
   {...}, so it swallowed whatever came after it whole. That is why the
   c-orb-travel-sm keyframes below looked dead — they were not merely
   unreferenced, they were never in the stylesheet. Chrome reported the sheet
   as fine throughout; the rule was just missing from cssRules. The whole
   sheet had two more } than { and now balances. */
/* The c-orb-travel-sm keyframes that used to sit here were never referenced
   by any animation-name, so the "small" radius they defined had never once
   been on screen. --r on .c-orb-c replaces them: a container query sets the
   variable and the one set of keyframes reads it. */
/* ── THE RING KEEPS ITS CARDS ON A PHONE ──
   This used to strip every satellite to a bare photo disc under 460px, on the
   reasoning that nine cards on a ~330px orbit have 89px of chord and a card
   with a name is ~70px tall, so the diagonal pairs touch. The ring is EIGHT,
   not nine, and eight changes the arithmetic enough to matter.
   Eight cards sit at exact multiples of 45 degrees, so the closest pair is
   separated by dx = r(1 - cos45) = .293r and dy = r sin45 = .707r. Two boxes
   miss each other when EITHER gap clears the box rather than both, so the
   condition is only .707r >= max(width, height): r >= 1.414 x the card's
   larger side. Fitting inside the container wants r + side/2 <= half the
   container, which together allow a side up to container/3.83.
   At 390px the orbit is 358 wide, so that ceiling is 93px — the card below
   measures well inside it, on a 37.5% radius, and carries the name and the
   status line the reader came for. A face alone was never what the ring was
   communicating; eight names is. */
@container orb (max-width: 460px) {
  .c-orb-c { width:24cqw; padding:1.8cqw 1.4cqw 1.4cqw; border-radius:3cqw;
             --r:37.5%; }
  .c-orb-ph { width:8.5cqw; height:8.5cqw; margin-bottom:1.6cqw; }
  .c-orb-c .c-avatar { width:8.5cqw; height:8.5cqw; font-size:3.8cqw;
                       margin-bottom:1.6cqw; }
  .c-orb-c b { font-size:clamp(9.5px,2.8cqw,11.5px); }
  .c-orb-c .c-orb-on { font-size:clamp(8px,2.3cqw,9px); }
}
/* BELOW ~310px THE CARDS GENUINELY DO NOT FIT, and this is the arithmetic
   saying so rather than a taste call. The name and the status line stop
   scaling at their clamp floors — 9.5px and 8px — so the card stops shrinking
   with the container while the radius keeps shrinking, and the radius the
   spacing needs passes the one the container can hold. Two of the eight would
   overlap. The disc is the honest fallback there, and it is the old treatment
   unchanged. */
@container orb (max-width: 310px) {
  .c-orb-c { width:15cqw; padding:1.2cqw; border-radius:50%; --r:36.5%; }
  .c-orb-c b, .c-orb-c .c-orb-on { display:none; }
  .c-orb-ph { width:12.6cqw; height:12.6cqw; margin-bottom:0; }
  .c-orb-c .c-avatar { width:12.6cqw; height:12.6cqw; font-size:5cqw;
                       margin-bottom:0; }
}
@media (prefers-reduced-motion:reduce) {
  .c-orb-c, .c-orb-on::before { animation:none; }
}

/* ── astrologer card ──────────────────────────────────── */
/* min-width:0 SO THE CARD CAN BE NARROWER THAN ITS WIDEST LINE. Every place
   this card is used puts it in a flex or grid item, where min-width defaults
   to auto — the card's own min-content — and that was 326px: the width of
   "English · Hindi · Bengali · Marathi · Telugu · Tamil" set nowrap. So the
   card had a 326px floor no matter what it was given.
   On /astrologers/ the roster grid has no scroller to absorb that, so a 320px
   phone was handed a 342px layout viewport and the entire page moved
   sideways. The language line already carries text-overflow:ellipsis for
   exactly this case; it simply never got the chance to use it.
   min-width:0 on the LANGUAGE line is not enough and was tried first: that
   lets the line shrink, but the card's own intrinsic width is still computed
   from its contents, and it is the card that has to be allowed to be small. */
.c-as { display:flex; flex-direction:column; padding:16px; min-width:0;
        border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface);
        /* This card is 272px in the landing rail and 358px in the roster on
           the same phone, so what fits is a question about the CARD, not the
           viewport. The rules that stack the rating line are keyed to this
           rather than to a media query, which had them stacking at 358 too. */
        container-type:inline-size; container-name:as; }
.c-as-h { display:flex; align-items:flex-start; gap:13px; }
.c-as-ph { width:64px; height:64px; border-radius:50%; flex:none;
           object-fit:cover; background:var(--surface-2);
           border:1px solid var(--line); }
.c-as-h .c-avatar { width:64px; height:64px; border-radius:50%; font-size:24px; }
.c-as-id { min-width:0; flex:1; }
/* ── THE WHOLE CARD OPENS THE ASTROLOGER ──
   A stretched link, not a wrapping <a>: the card already contains two
   buttons, and an anchor inside an anchor is invalid HTML that browsers
   resolve by closing the outer one early — which would leave Chat and Voice
   sitting OUTSIDE the card link, working by accident and differently per
   browser.
   So the name is the real link and its ::after covers the card. One
   focusable element, one accessible name, and the buttons ride above it on
   z-index so they still get their own clicks. */
.c-as { position:relative; }
.c-as-n { color:inherit; text-decoration:none; }
.c-as-n:hover { text-decoration:underline; text-underline-offset:2px; }
.c-as-n::after { content:''; position:absolute; inset:0; z-index:1; }
/* Everything interactive, and the tags, above the overlay. Without the tags
   a reader cannot select the text on the card, which is the usual complaint
   about this pattern. */
.c-as:has(.c-as-n) .c-as-buy,
.c-as:has(.c-as-n) .c-as-tags { position:relative; z-index:2; }
/* The keyboard ring belongs on the card, not on three words inside it. */
.c-as:focus-within { outline:2px solid var(--gold); outline-offset:2px;
                     border-radius:var(--r-card); }
.c-as-n:focus-visible { outline:none; }
/* ── THE CARD AT RAIL WIDTH ──
   272px is narrower than this card has ever been asked to be, and three
   things broke at once: "· 130 questions answered" wrapped to three ragged
   lines beside the stars, a third tag pushed the tag row onto two lines, and
   six language names took two more. The card came out tall, cramped and
   mostly wrapping.
   Each is fixed where it breaks rather than by shrinking the type: the count
   takes its own line under the stars, the tag row keeps the two that fit, and
   the languages stay on one line and trail off. The detail page carries all
   of them, and this card's job is to be chosen from. */
/* ── ONLY WHEN THE CARD ITSELF IS NARROW ──
   "· 130 questions answered" beside five stars and a 4.6 needs about 246px of
   text column. The rail card has 190 and the roster card on the same phone has
   269, so one has to stack and the other does not — and keying this to the
   viewport stacked both, costing 20px on all twenty-four roster cards for a
   line that fitted. */
@container as (max-width: 300px) {
  .c-as-r { flex-wrap:wrap; row-gap:2px; }
  .c-as-r i { flex:1 0 100%; }
  /* The middot joined the count to the rating on one line. On its own line it
     is punctuation with nothing before it, so it goes with the wrap. */
  .c-as-sep { display:none; }
}
@media (max-width:640px) {
  /* .c-as .c-as-tags, NOT .c-as-tags. The base rule sets flex-wrap:wrap and
     is declared LATER in this file, so at equal specificity it wins and the
     nowrap here did nothing — it only looked right because the third tag is
     hidden below, leaving nothing to wrap. Two long tags would still have
     broken the row. Fourth time a media query in this file has lost a
     cascade fight; raising the specificity is the fix that survives the
     block being moved again. */
  .c-as .c-as-tags { flex-wrap:nowrap; overflow:hidden; min-width:0; }
  .c-as-t:nth-child(n+3) { display:none; }
  /* min-width:0 OR THE CARD CANNOT SHRINK. Both of these are nowrap, and a
     flex item's min-width defaults to auto — its min-content — so the card's
     own minimum became the full width of "English · Hindi · Bengali ·
     Marathi · Telugu · Tamil" laid out on one line: 326px, whatever the
     viewport. overflow:hidden does not change that; only min-width:0 does.
     On /astrologers/ the roster grid has no scroller to absorb it, so a
     320px phone was handed a 342px layout viewport and the whole page moved
     sideways. On the landing page the rail hid it by scrolling, which is why
     every rail card had quietly been 326px rather than the width the rail
     asked for.
     Fourth time this exact trap has bitten in this codebase — grid item,
     marquee, flex item, and now the flex item inside the component. */
  .c-as-lang { white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
               min-width:0; }
}
.c-as:has(.c-as-n):hover { border-color:var(--gold); }
.c-as-id > b { display:flex; align-items:center; gap:5px; font-size:16px;
               font-weight:750; letter-spacing:-.01em; }
.c-as-v { width:15px; height:15px; flex:none; color:var(--good); }
.c-as-sp { display:block; font-size:13.5px; color:var(--ink-2); margin-top:1px; }
.c-as-r { display:flex; align-items:center; gap:6px; margin-top:6px; }
.c-as-r > b { font-size:13px; font-weight:750; }
.c-as-r i { font-style:normal; font-size:12.5px; color:var(--ink-3); }
.c-as-st { position:relative; display:inline-block; line-height:0; flex:none; }
.c-as-st-b { display:flex; gap:1px; color:var(--line); }
.c-as-st-f { display:flex; gap:1px; position:absolute; inset:0;
             overflow:hidden; color:var(--gold); }
.c-as-st-b svg, .c-as-st-f svg { fill:currentColor; stroke:currentColor;
                                 stroke-width:1; }
.c-as-tags { display:flex; flex-wrap:wrap; gap:5px; margin-top:13px; }
.c-as-t { font-size:12px; font-weight:600; padding:5px 10px;
          border-radius:var(--r-sm); color:var(--ink-2);
          background:var(--surface-2); }
.c-as-lang { font-size:13px; color:var(--ink-3); margin-top:11px; }
.c-as-buy { display:grid; grid-template-columns:1fr 1fr; gap:8px;
            margin-top:auto; padding-top:14px; }
.c-as-b { display:inline-flex; align-items:center; justify-content:center;
          gap:7px; font-size:14px; text-decoration:none; padding:0 10px; }
.c-as-b svg { width:17px; height:17px; }

/* ── THE CARD ON A PHONE ──
   267px each, twenty-four of them, one per row: seven thousand pixels of
   roster on /astrologers/ and better than eight screens of scrolling. The
   desktop card is 241px in a three-column grid where that reads as a tile;
   stacked full-width on a phone the same spacing reads as air.
   Nothing is removed here — the photo, the name, the specialism, the rating,
   the count, two tags, the languages and both rates all stay, because this
   card exists to be chosen from and every one of those is a reason to choose.
   Only the spacing tightens, and the photo comes down to a size that still
   shows a face at arm's length.
   AFTER the base rules it overrides. Same cascade trap as everywhere else in
   this file. */
@media (max-width:640px) {
  .c-as { padding:13px; }
  .c-as-ph, .c-as-h .c-avatar { width:52px; height:52px; }
  .c-as-h .c-avatar { font-size:20px; }
  .c-as-h { gap:11px; }
  .c-as-id > b { font-size:15.5px; }
  .c-as-sp { font-size:13px; }
  .c-as-r { margin-top:4px; }
  .c-as-tags { margin-top:9px; }
  .c-as-t { padding:4px 9px; }
  .c-as-lang { margin-top:7px; font-size:12.5px; }
  .c-as-buy { padding-top:10px; gap:7px; }
}


/* ── service strip ────────────────────────────────────── */
.c-sv { display:grid; gap:10px; grid-template-columns:repeat(2,minmax(0,1fr)); }
@media (min-width:560px) { .c-sv { grid-template-columns:repeat(3,minmax(0,1fr)); } }
@media (min-width:900px) { .c-sv { grid-template-columns:repeat(5,minmax(0,1fr)); } }
.c-sv-i { position:relative; display:block; padding:14px 14px 15px;
          border:1px solid var(--line); border-radius:var(--r-card);
          background:var(--surface); text-decoration:none; color:var(--ink); }
.c-sv-i:hover { border-color:var(--gold); background:var(--gold-bg); }
.c-sv-ic { display:grid; place-items:center; width:38px; height:38px;
           border-radius:11px; color:var(--gold-dk); background:var(--gold-bg);
           margin-bottom:11px; }
.c-sv-i:hover .c-sv-ic { background:var(--surface); }
.c-sv-i b { display:block; font-size:14.5px; font-weight:700; line-height:1.3; }
.c-sv-i i { display:block; font-style:normal; font-size:12.5px; line-height:1.45;
            color:var(--ink-3); margin-top:3px; padding-right:20px; }
.c-sv-go { position:absolute; right:12px; bottom:12px; color:var(--ink-3);
           line-height:0; }
.c-sv-i:hover .c-sv-go { color:var(--gold-dk); }
/* ── ON A PHONE IT IS A RAIL, WHICH IS WHAT THIS ALWAYS MEANT TO BE ──
   service_strip's own docstring says it "only becomes a swipe on a phone
   where a swipe is expected". It never did: two columns of 173px at 390px,
   where "Full chart in 20 seconds" takes three lines, "Tithi, Rahu kaal,
   choghadiya" takes two, and no two cards are the same height. Ten tiles
   that way is four screens of ragged grid.

   One row at a fixed width instead. Every card the same height because every
   card has the same measure, and the ten of them cost one screen.

   IT BLEEDS TO THE EDGES. Negative margin cancels .l-wrap's 16px gutter and
   the padding puts it back inside the scroller, so the first card still
   lines up with the heading above it AND a card can sit half-off the right
   edge. A rail that stops neatly at the gutter looks like a row that ends. */
@media (max-width:640px) {
  /* scroll-padding, or the padding is eaten before anybody sees it.
     scroll-snap-align:start snaps a card's leading edge to the SNAPPORT,
     and the snapport defaults to the padding edge — so with mandatory snap
     the browser scrolls the rail by exactly padding-left on load and the
     first card ends up flush against the screen while its own heading sits
     16px in. The padding was there and measured 16px the whole time; the
     scroller had simply moved past it. */
  .c-sv { display:flex; overflow-x:auto; gap:11px;
          scroll-snap-type:x mandatory; scrollbar-width:none;
          margin-inline:-16px; padding:2px 16px 8px;
          scroll-padding-inline:16px; }
  .c-sv::-webkit-scrollbar { display:none; }
  /* CENTRED, because one card sets the height for all ten. "Talk to an
     Astrologer" is the only two-line title in the strip, so every other card
     carries its surplus — top-aligned that reads as a void under the text,
     centred it reads as padding. Clamping the title instead would give
     "Talk to an As…", which is worse than a little air. */
  .c-sv-i { flex:0 0 152px; scroll-snap-align:start; padding:18px 12px 18px;
            display:flex; flex-direction:column; justify-content:center;
            align-items:center; text-align:center; }
  /* The icon centres with everything else. It was a 38px square hard against
     the left edge of a 152px tile, which reads as a row that lost its second
     column — on a wide grid there IS a second column of text beside it, and
     at rail width there is not. */
  .c-sv-ic { margin-bottom:12px; }
  /* The chevron is an affordance for a card you click. In a rail the swipe
     is the affordance and the arrow crowds a 152px card. */
  .c-sv-go { display:none; }
  /* TWO LINES, SO NO CARD IS AN OUTLIER. Flex stretches every card to the
     tallest, and the tallest was "Shubh dates for 22 kinds of work" at three
     lines — which left ~110px of empty card under "Free Kundli" and every
     other short one. Clamping the detail makes the tallest two lines, so the
     row is shorter and nothing is padded out to match a single long string. */
  .c-sv-i { padding:13px 13px 14px; }
  .c-sv-i i { padding-right:0; display:-webkit-box; -webkit-line-clamp:2;
              -webkit-box-orient:vertical; overflow:hidden; }
}
/* PLACED AFTER THE COMPONENT, NOT INSIDE IT. Written above `.c-sv-i i
   { display:block }` the clamp lost to it — a media query adds no
   specificity, so the later of two equal rules wins and the detail kept
   running to three lines. Two cards were doing that and stretching every
   other card in the row to match, which is the ~100px of empty card this
   block exists to remove. */


/* ── day timeline ─────────────────────────────────────── */
.c-tl { border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); overflow:hidden; }
.c-tl-head { display:flex; justify-content:space-between; padding:11px 16px;
             border-bottom:1px solid var(--line-soft); background:var(--surface-2); }
.c-tl-sun b { font-size:14px; font-weight:750; }
.c-tl-sun i { font-style:normal; font-size:11px; color:var(--ink-3); margin-left:7px; }
.c-tl-r2 { text-align:right; }
/* Two columns: the labels, and one track column every row shares. The overlay
   spans all rows inside that second column, so a percentage on it means a
   percentage of the timeline. */
.c-tl-body { position:relative; display:grid; grid-template-columns:84px 1fr;
             gap:7px 10px; padding:14px 16px 16px; align-items:center; }
/* Two overlay layers, not one. A single layer before the rows put the now
   line behind every bar; a single layer after them put the night band on top
   of the text. Night sits behind, the marker in front. */
/* 32px of gutter, holding one row of markers: "NIGHT" and moonrise sit on
   the same line, just above the hour axis. Snug against the axis is what
   makes the moon read as a time rather than as a stray glyph — see
   .c-tl-2mk for the one day a month that cannot have it. */
.c-tl-body { --lbl:84px; padding-top:32px; }
.c-tl-over { position:absolute; top:32px; bottom:16px;
             left:calc(16px + var(--lbl) + 10px); right:16px;
             pointer-events:none; }
.c-tl-front { z-index:4; }
.c-tl-night { position:absolute; top:0; bottom:0; left:var(--l); right:0;
              background:linear-gradient(90deg,
                  color-mix(in srgb, var(--ink) 9%, transparent) 0 1.5px,
                  color-mix(in srgb, var(--ink) 4.5%, transparent) 1.5px);
              border-left:0; }
.c-tl-night::before { content:''; position:absolute; left:0; top:0; bottom:0;
              width:1.5px; background:var(--gold-lt); opacity:.55; }
.c-tl-night i { position:absolute; top:-19px; left:0; transform:translateX(-50%);
                font-style:normal; font-size:8.5px; font-family:var(--font-mono);
                letter-spacing:.1em; text-transform:uppercase; color:var(--gold-dk);
                background:var(--surface); padding:0 6px; white-space:nowrap;
                border-radius:var(--r-pill); }
.c-tl-lbl { font-family:var(--font-mono); font-size:9.5px; letter-spacing:.1em;
            text-transform:uppercase; color:var(--ink-3); }
.c-tl-track { position:relative; height:30px; }
.c-tl-b { position:absolute; top:0; bottom:0; left:var(--l); width:var(--w);
          display:flex; align-items:center; justify-content:center;
          border-radius:8px; background:var(--gold-bg);
          border:1px solid var(--line); overflow:hidden;
          container-type:inline-size;
          transform-origin:left center; animation:c-tl-in .5s var(--d) both ease-out; }
/* IN FLOW, NOT ABSOLUTE, and that is what makes the overlap impossible rather
   than unlikely. The end time used to be position:absolute at the right of the
   bar while the name was centred in the whole width, so the two could only be
   kept apart by hiding the end time below a threshold — 116px, which a 121px
   karana walked straight past and printed "Gara" through "10:33 AM".
   A px threshold cannot be right for both "Gara" and "Uttara Bhadrapada"
   anyway. As flex items the name takes what is left and centres inside it, so
   there is no width at which they can reach each other. */
.c-tl-b b { flex:1 1 auto; min-width:0; text-align:center;
            font-size:12px; font-weight:650; white-space:nowrap;
            overflow:hidden; text-overflow:ellipsis; padding:0 8px; }
.c-tl-e { flex:none; display:flex; align-items:center; padding-right:7px;
          font-size:10px; text-decoration:none; color:var(--ink-3);
          font-family:var(--font-mono); }
.c-tl-axis { height:16px; }
/* The rule is a pseudo-element, not a background on the tick itself. With a
   background there the hour label became a child of a 1px painted box and
   measured its contrast against --line at 4.01:1, when it actually sits on
   the card. */
.c-tl-t { position:absolute; top:0; bottom:0; left:var(--l); width:1px; }
.c-tl-t::before { content:''; position:absolute; inset:0; background:var(--line); }
.c-tl-t i { position:absolute; top:0; left:3px; font-style:normal;
            font-size:9.5px; color:var(--ink-3); font-family:var(--font-mono); }
/* ── EVERY OTHER HOUR, ON A PHONE ──
   The ticks are placed by percentage, so at 390px the track is about 306px
   and twelve of them land 25px apart — with "10am" at 9.5px mono measuring
   about 26px. They collided into "10am12pm2pm" and the NOW pill sat on top
   of "6am". Shipped that way and visible on production today.

   Hiding alternate LABELS rather than alternate TICKS: the rules are what
   make the row read as an axis and they cost nothing at 1px, while the
   labels are the only part that needs room. Halving them gives each one
   ~51px, which also buys the point back up to 10.5px — a legible size for
   the one row on this component a reader actually reads across. */
@media (max-width:640px) {
  /* ── THE DAY SCROLLS, RATHER THAN LOSING HALF ITS HOURS ──
     The first fix here hid alternate hour labels so the surviving ones had
     room. It stopped them colliding and it also threw away half the axis: a
     panchang is read ACROSS, and "when does this tithi end" is answered by
     the labels, not by the bars.
     So the card scrolls instead and every hour comes back. .c-tl is the
     scroller and .c-tl-body carries the min-width, NOT the other way round —
     the night band and the now marker are absolutely positioned inside the
     body, and an overflow container sizes its absolute children to the
     visible box rather than the scrollable one, which would have cut both
     off at the right edge of the screen.
     The row labels stay put while it moves. Scrolling a table sideways and
     losing the column that says which row you are on is the usual way this
     pattern fails. */
  .c-tl { overflow-x:auto; scrollbar-width:none; }
  .c-tl::-webkit-scrollbar { display:none; }
  /* 760, NOT 620. The day already scrolls sideways here, so the only cost of
     a wider track is a longer scroll — and 620 gave each of the thirteen
     choghadiya 36px for names that need 48 at the 11px floor, so "Udvega"
     and "Shubha" read as "Udv…" and "Shu…". Ten of them were clipped at the
     old 9.5px too; this is the width the row has always wanted. */
  .c-tl-body { min-width:840px; }
  .c-tl-head { min-width:840px; }
  /* A FROZEN COLUMN, NOT A CHIP RIDING ON THE BARS. Sticky alone gave the
     label its content box and nothing else: 14px of text pinned in the middle
     of a 30px bar, so 8px of tithi showed above it and 8px below and the name
     centred in the bar slid out from under it. It read as a label lying on top
     of the timeline, which is what it was.
     Three things make it a column instead. align-self:stretch so it is as tall
     as the row it names. The negative margins so its background reaches the
     card's left padding and across the 10px grid gap — pinned at left:16px it
     left a 16px gutter for bars to scroll through beside it, and the gap was a
     second one. And the shadow, so the edge the day passes under is visible
     rather than a hard seam between two backgrounds.
     left:0 rather than 16px because the box now starts at the card edge. */
  .c-tl-lbl { position:sticky; left:0; z-index:5; background:var(--surface);
              align-self:stretch; display:flex; align-items:center;
              margin-left:-16px; margin-right:-10px;
              padding-left:16px; padding-right:10px;
              box-shadow:6px 0 7px -6px
                  color-mix(in srgb, var(--ink) 30%, transparent);
              /* AND ACROSS THE GAP BETWEEN ROWS. align-self:stretch makes
                 this as tall as its own row and no taller, and .c-tl-body
                 has a 7px row gap — so between every pair of labels there
                 was a 7px band of nothing, and the day's bars slid through
                 it as the track scrolled under the column. Half the gap
                 each way, and the labels meet. */
              margin-top:-4px; margin-bottom:-4px;
              padding-top:4px; padding-bottom:4px; }
  .c-tl-t i { font-size:10px; }
}
/* After .c-tl-e, not before it. These rules and `.c-tl-e { display:flex }` have
   the same specificity, so whichever comes last wins — placed above, the
   container query was silently overridden and a 66px bar printed its name and
   its end time on top of each other. */
/* Still hidden on a narrow bar, but now for legibility rather than to stop a
   collision: below this the name has so little left that it truncates to an
   ellipsis, and a name is worth more than an end time already on the axis. */
@container (max-width:116px) { .c-tl-e { display:none; } }
/* One step down before giving up, then hidden outright. The hide threshold
   was 44px, which left a window — 44 to 66 — where a name neither fitted nor
   went away, and in it "Gulika" printed at 52px needing 53 and "Ekadashi" at
   56px needing 60. Both shipped as an ellipsis.
   Hiding is the right failure and truncating is the wrong one: a reader who
   sees no name looks at the row label or the cards under the bar, and a
   reader who sees "Ekadash…" has been told the wrong tithi. As the .c-tl-e
   note above says, no px threshold can be right for both "Gara" and "Uttara
   Bhadrapada" — checks/muhurat.mjs measures the real thing at four widths
   across seven days and fails on any name that is visibly cut. */
@container (max-width:110px) { .c-tl-b b { font-size:10px; padding:0 3px; } }
@container (max-width:66px) { .c-tl-b b { opacity:0; } }

.c-tl-chog { height:22px; }
.c-tl-c { position:absolute; top:0; bottom:0; left:var(--l); width:var(--w);
          display:flex; align-items:center; justify-content:center;
          border-radius:5px; background:var(--bad-bg); overflow:hidden;
          animation:c-tl-in .5s .5s both ease-out; transform-origin:left center; }
.c-tl-c.c-tl-good { background:var(--good-bg); }
.c-tl-c b { font-size:9.5px; font-weight:650; color:var(--bad);
            white-space:nowrap; overflow:hidden; padding:0 3px; }
/* .c-tl-c.c-tl-good, not .c-tl-good. Unscoped, this recoloured the name inside
   any toned BAR too, because a bar and a choghadiya chip now share the tone
   class and `.c-tl-good b` outranks nothing it needs to. */
.c-tl-c.c-tl-good b { color:var(--good); }
/* A toned bar keeps --ink for its name and puts the tone in the fill and a
   left edge. Colouring the text instead measured 4.55:1 on dark — over the
   line, but by 0.05, and the whole point of the row is that it is readable at
   a glance. The 3px edge also means the two tones are still distinguishable
   without relying on hue alone. */
.c-tl-b.c-tl-bad { background:var(--bad-bg); border-color:var(--bad);
                   border-left:3px solid var(--bad); }
.c-tl-b.c-tl-good { background:var(--good-bg); border-color:var(--good);
                    border-left:3px solid var(--good); }
.c-tl-b.c-tl-bad b, .c-tl-b.c-tl-good b { color:var(--ink); }
.c-tl-now { display:none; position:absolute; top:0; bottom:0;
            left:var(--l); width:2px;
            background:var(--gold-solid); border-radius:2px; z-index:3;
            animation:c-tl-now 2.4s ease-in-out infinite; }
.c-tl-now i { position:absolute; top:-19px; left:50%; transform:translateX(-50%);
              font-style:normal; font-size:8.5px; font-family:var(--font-mono);
              letter-spacing:.1em; text-transform:uppercase;
              color:var(--gold-solid-text); background:var(--gold-solid);
              padding:1px 5px; border-radius:4px; }
/* MOONRISE GOES IN THE GUTTER, WITH "NIGHT". At top:1px it sat inside the
   content layer, and the first thing in the content layer is .c-tl-axis —
   the hour ticks. So the moon landed on top of whichever hour it rose in
   and the reader got "8p🌙" for a label. The 32px the body reserves above
   the tracks is exactly what this row of markers is for: .c-tl-night i
   already climbs into it with the same negative top.
   The surface chip is what keeps it legible when moonrise falls close to
   sunset and the two markers meet — the same trick .c-tl-night i uses to
   sit over the ticks without being read through them. */
/* -22 puts the chip's bottom 2px above the hour axis, level with "NIGHT". */
.c-tl-moon { position:absolute; top:-22px; left:var(--l); z-index:3;
             color:var(--ink-3); transform:translateX(-50%); line-height:0;
             background:var(--surface); padding:3px; border-radius:50%; }
.c-tl-moon svg { display:block; width:14px; height:14px; fill:none;
                 stroke:currentColor; stroke-width:1.8; }
/* THE DAYS THEY COLLIDE, AT THE WIDTHS THEY COLLIDE AT. Moonrise runs from
   sunrise at new moon to SUNSET at full, so roughly once a month the moon
   lands on the sunset line and one shared row reads "N(moon)T". The build
   picks the class — see day_timeline for the measured table — and these
   decide where it applies. -41 rather than -38 because the chip is 20px and
   "NIGHT" is 17px tall at -19: -38 left them sharing a single pixel, which
   tl-overlap.mjs caught at 768px within a minute of being taught the rule. */
.c-tl-2mk, .c-tl-2mk-md, .c-tl-2mk-sm { padding-top:46px; }
.c-tl-2mk > .c-tl-over, .c-tl-2mk-md > .c-tl-over,
.c-tl-2mk-sm > .c-tl-over { top:46px; }
.c-tl-2mk .c-tl-moon, .c-tl-2mk-md .c-tl-moon,
.c-tl-2mk-sm .c-tl-moon { top:-41px; }
/* ...and it is given back the moment the track is wide enough to hold both
   on one line. A second line where there is room is the floating-moon bug,
   which shipped twice — once by stacking every day and once by stacking on
   a threshold set for the narrowest screen. */
@media (min-width:1024px) {
  .c-tl-2mk-sm { padding-top:32px; }
  .c-tl-2mk-sm > .c-tl-over { top:32px; }
  .c-tl-2mk-sm .c-tl-moon { top:-22px; }
}
@media (min-width:1280px) {
  .c-tl-2mk-md { padding-top:32px; }
  .c-tl-2mk-md > .c-tl-over { top:32px; }
  .c-tl-2mk-md .c-tl-moon { top:-22px; }
}
@keyframes c-tl-in { from { transform:scaleX(0); opacity:0; }
                     to   { transform:scaleX(1); opacity:1; } }
@keyframes c-tl-now { 0%,100% { opacity:1; } 50% { opacity:.35; } }
/* The marker is hidden until the browser has placed it — see TIMELINE_JS. */
.c-tl-now[style*="--l"] { display:block; }
.c-tl-still .c-tl-b, .c-tl-still .c-tl-c { animation:none; }
@media (prefers-reduced-motion:reduce) {
  .c-tl-b, .c-tl-c, .c-tl-now { animation:none; }
}
@media (max-width:700px) {
  /* 70, NOT 62: THE COLUMN HAS TO FIT THE TYPE THAT IS ACTUALLY IN IT.
     62 was measured against the 8.5px on the next line, and the signed-in
     shell floors every label on the page to 11 — so "Choghadiya" wanted 67px
     of a 62px column and, being one word, had nowhere to wrap. It spilled
     5px to the right, over the very bands this column's shadow exists to
     cover. Nothing caught it: the label is not clipped, because the column
     does not clip; not small, because it had just been raised; and the page
     does not scroll sideways, because the track scrolls inside itself. It
     costs the track 8px of a viewport the track already scrolls past. */
  .c-tl-body { --lbl:70px; grid-template-columns:70px 1fr; gap:6px; }
  .c-tl-over { left:calc(16px + var(--lbl) + 6px); }
  .c-tl-lbl { font-size:8.5px; }
  .c-tl-b b { font-size:11px; padding:0 5px; }
  .c-tl-e { display:none; }
  /* The floor above puts this back to 11px; what it can still have is the
     chip's own 6px of padding, which a name needs more than the chip needs
     breathing room. */
  .c-tl-c b { padding:0; }
}

/* ── rashifal grid ────────────────────────────────────── */
.c-rf { display:grid; gap:10px; grid-template-columns:1fr; }
@media (min-width:600px) { .c-rf { grid-template-columns:repeat(2,minmax(0,1fr)); } }
@media (min-width:1000px) { .c-rf { grid-template-columns:repeat(3,minmax(0,1fr)); } }
.c-rf-i { display:flex; flex-direction:column; height:100%; padding:14px;
          border:1px solid var(--line); border-radius:var(--r-card);
          background:var(--surface); text-decoration:none; color:var(--ink); }
.c-rf-i:hover { border-color:var(--gold); background:var(--gold-bg); }
.c-rf-h { display:flex; align-items:center; gap:10px; }
.c-rf-h .c-sym { flex:none; color:var(--gold-dk); }
.c-rf-n { flex:1; min-width:0; }
.c-rf-n b { display:block; font-size:15px; font-weight:750; }
.c-rf-n i { display:block; font-style:normal; font-size:11.5px; color:var(--ink-3); }
.c-rf-h .c-as-st { align-self:flex-start; margin-top:2px; }
.c-rf-h .c-as-st svg { width:12px; height:12px; }
.c-rf-p { font-size:12.5px; line-height:1.5; color:var(--ink-2);
          margin:11px 0 12px; flex:1; }
.c-rf-sc { display:grid; gap:5px; }
.c-rf-s { display:flex; align-items:center; gap:8px; }
.c-rf-s i { flex:none; width:46px; font-style:normal; font-size:10px;
            color:var(--ink-3); font-family:var(--font-mono);
            letter-spacing:.06em; text-transform:uppercase; }
.c-rf-t { flex:1; height:5px; border-radius:3px; background:var(--line-soft);
          overflow:hidden; }
.c-rf-t b { display:block; height:100%; width:var(--v); border-radius:3px;
            background:var(--gold); transform-origin:left center;
            animation:c-rf-fill .6s .1s both ease-out; }
.c-rf-f { display:flex; justify-content:space-between; align-items:baseline;
          gap:8px; margin-top:12px; padding-top:10px;
          border-top:1px solid var(--line-soft); }
.c-rf-f em { font-style:normal; font-size:12px; font-weight:700;
             color:var(--gold-dk); }
.c-rf-x { font-size:11.5px; color:var(--ink-3); }
@keyframes c-rf-fill { from { transform:scaleX(0); } to { transform:scaleX(1); } }
@media (prefers-reduced-motion:reduce) { .c-rf-t b { animation:none; } }

/* ── marquee ──────────────────────────────────────────── */
/* No negative-margin bleed. It made the track 32px wider than its parent, so
   at 320px the page ran 26px over — the clip cannot help when the clipping box
   is itself wider than the viewport. The mask already fades the edges, which
   is what the bleed was for. */
/* THE EDGE FADE IS 18px, AND IT USED TO BE 4%. A percentage is a share of
   the track, so the fade grew with the page: 42px at 1056px wide, dissolving
   a whole gold button from solid to nothing. A large saturated rectangle
   fading out over a dark ground does not read as "the row continues" — it
   reads as a glow, which is what it was reported as. Short and fixed says
   the same thing at every width, and a card cut at the edge reads as cut. */
.c-mq { overflow:hidden; min-width:0;
        --fade:18px;
        -webkit-mask-image:linear-gradient(90deg,transparent,#000 var(--fade),
                #000 calc(100% - var(--fade)),transparent);
                mask-image:linear-gradient(90deg,transparent,#000 var(--fade),
                #000 calc(100% - var(--fade)),transparent); }
.c-mq-t, .c-mq-dup { display:flex; gap:12px; }
.c-mq-t { width:max-content; animation:c-mq-run var(--dur) linear infinite; }
.c-mq:hover .c-mq-t, .c-mq:focus-within .c-mq-t { animation-play-state:paused; }

/* ── the steered one ──
   A SCROLLER, NOT A TRANSFORM. The belt is driven by adding to scrollLeft,
   which is the one thing a wheel, a drag, an arrow key and the two buttons
   below can all move as well — so the reader can go back to a card that has
   gone past instead of waiting out the loop. No animation on the track at
   all: the two mechanisms would fight, and this one is also what runs when
   the script never arrives. */
.c-mq-s { overflow-x:auto; overflow-y:hidden; scrollbar-width:none;
          overscroll-behavior-x:contain; }
.c-mq-s::-webkit-scrollbar { display:none; }
.c-mq-s .c-mq-t { animation:none; }
.c-mq-s:focus-visible { outline:2px solid var(--gold); outline-offset:3px; }
/* THE ARROWS GET THEIR OWN GUTTER. At left:4px / right:4px they overlapped
   the first and last card by 38x38 — measured — right over the cover and
   title of whichever report the track opened with. Every card was clickable
   and one of them had a scroll button lying on the part of it a reader aims
   at, so the report the shelf leads with was the one that would not open.
   Reported as "start here report is not clickable".
   Padding on the wrapper rather than a smaller arrow or a nudge: an overlay
   that mostly misses is the same bug with a smaller target. */
.c-mq-w { position:relative; padding-inline:46px; }
.c-mq-nav { position:absolute; top:50%; transform:translateY(-50%); z-index:3;
        display:grid; place-items:center; width:38px; height:38px; padding:0;
        border-radius:50%; border:1px solid var(--line); cursor:pointer;
        background:var(--surface); color:var(--ink);
        box-shadow:0 2px 10px rgba(0,0,0,.16); }
.c-mq-nav:hover { border-color:var(--gold); color:var(--gold-dk); }
.c-mq-nav[data-mq-d="-1"] { left:0; }
.c-mq-nav[data-mq-d="1"] { right:0; }
/* The arrows are the pointer affordance and a keyboard reader has the
   scroller itself, which is focusable and takes arrow keys. Announcing both
   routes to the same twenty cards is noise. */
@media (max-width:560px) {
  .c-mq-nav { display:none; }
  /* and the gutter goes with them, or the track loses 92px to nothing on the
     screen that can least afford it. */
  .c-mq-w { padding-inline:0; }
}
.c-mq-i { flex:none; width:300px; }
/* THE CARDS IN A TRACK END ON THE SAME LINE. .c-mq-i stretches to the tallest
   item because the track is a flex row — but a BLOCK child of it does not
   inherit that height, so a card carrying a launch offer stood 54px taller
   than its neighbours and its button sat that far below theirs. The grid at
   /reports/ never showed this: grid items stretch and their block children
   fill, so the same cards lined up there and only the moving track was
   ragged. One flex box hands the height down; .c-rp-pay's margin-top:auto
   already does the rest. */
.c-mq-i:has(.c-rp) { display:flex; position:relative; }
.c-mq-i:has(.c-rp) > .c-rp { width:100%; }
/* ROOM FOR WHAT DRAWS OUTSIDE A CARD. This was for the "Start here" pill,
   which sat at top:-9px and lost 8px to the track's overflow clip — the clip
   the loop needs. The pill has gone, and the rule has not, because the pill
   was not the only thing outside the card's box: .c-cat-hit covers the whole
   card and takes focus, and its focus ring is drawn at outline-offset:3px.
   A keyboard reader tabbing the track would have the ring cut off at the top
   and bottom of every card, which is the one place a ring must not be cut.
   Both sides now, and keyed on carrying a card at all rather than on a badge
   that no longer exists. */
.c-mq:has(.c-rp) { padding-block:5px; }
@keyframes c-mq-run { to { transform:translateX(-50%); } }
/* Reduced motion stops the travel and hands it back as a normal scroller —
   the content must still be reachable, not frozen at whatever it was showing. */
@media (prefers-reduced-motion:reduce) {
  .c-mq { overflow-x:auto; -webkit-mask-image:none; mask-image:none;
          scroll-snap-type:x mandatory; }
  .c-mq-t { animation:none; }
  .c-mq-dup { display:none; }
  .c-mq-i { scroll-snap-align:start; }
}
@media (max-width:560px) { .c-mq-i { width:250px; } }

.c-px-wait { color:transparent !important; border-radius:5px;
             /* The text is still there and still measured, so the reveal
                cannot move anything — the bar is exactly the width of the
                figure that lands in it. */
             background-image:linear-gradient(90deg,
               var(--surface-2) 0%, var(--line) 40%,
               var(--line) 60%, var(--surface-2) 100%);
             background-size:220% 100%;
             animation:c-px-sh 1.15s linear infinite; }
/* Inside a filled button the two neutrals disappear, so the bar borrows the
   button's own foreground at low alpha instead. */
.c-btn-primary .c-px-wait { background-image:linear-gradient(90deg,
    rgba(255,255,255,.14) 0%, rgba(255,255,255,.40) 40%,
    rgba(255,255,255,.40) 60%, rgba(255,255,255,.14) 100%); }
@keyframes c-px-sh { from { background-position:110% 0; }
                     to   { background-position:-110% 0; } }
@media (prefers-reduced-motion:reduce) {
  .c-px-wait { animation:none; }
}

/* ── the report catalogue ─────────────────────────────── */
.c-cat-chips { display:flex; flex-wrap:wrap; gap:8px; margin-bottom:16px; }
.c-cat-chip { font:inherit; cursor:pointer; display:inline-flex; gap:8px;
        align-items:center; padding:8px 14px; min-height:40px; font-size:13px;
        border:1px solid var(--line); border-radius:var(--r-pill);
        background:var(--surface); color:var(--ink); }
.c-cat-chip:hover { border-color:var(--gold-lt); }
/* 40px, and the rule is 44 — the six category filters are how a phone reader
   gets from twelve reports to the two they want. */
@media (pointer:coarse) { .c-cat-chip { min-height:44px; } }
.c-cat-chip i { font-style:normal; font-family:var(--font-mono); font-size:11px;
        color:var(--ink-3); }
.c-cat-i { position:relative; display:flex; }
.c-cat-i > .c-rp { width:100%; height:100%; }
/* The whole card, as one target. It sits under the card's own button, which
   is lifted above it — so a press anywhere reads the description and a press
   on the button buys. */
.c-cat-hit { position:absolute; inset:0; z-index:1;
        border-radius:var(--r-card); }
.c-cat-hit:focus-visible { outline:2px solid var(--gold); outline-offset:3px; }
/* ── THE PRICE STAYS ON THE BUTTON HERE TOO ──
   This was `.c-cat-i .c-rp-b > b { display:none }`, with no reason written
   down. It was survivable while the button read "Get this report ₹199":
   hiding the figure left a whole label behind. It stopped being survivable
   the moment the label became "Only ₹199" — the dashboard band shipped three
   buttons reading "Only" and nothing else, which is precisely the state the
   data-rp-alt fallback exists to prevent, arrived at by a route that
   fallback cannot see. A CSS rule in another file hid the number; no code
   removed it.
   The band is the same card as the shelf and now says the same thing. */
.c-rp-b { position:relative; z-index:2; }
.c-cat-sr { position:absolute; width:1px; height:1px; overflow:hidden;
        clip-path:inset(50%); white-space:nowrap; }
/* ── TWO ACROSS ON A PHONE, BUT NOT ON THE SMALLEST ONE ──
   Twelve cards one-per-row ran this shelf to twelve screens of scrolling,
   each card 358px wide to hold a 172px-wide thing. Two across halves it.
   360 rather than "all phones": at 320 the card comes out 137px, which
   stretched the layout viewport to 324 and clipped two descriptions —
   caught by the phone sweep, which is why the breakpoint is where it is
   and not where it looked right. Every current handset is 360 or wider;
   below that the single column is still the correct answer. */
.c-cat-grid { display:grid; gap:14px; grid-template-columns:1fr; }
@media (min-width:360px) {
  .c-cat-grid { grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (min-width:700px) { .c-cat-grid { grid-template-columns:1fr 1fr; } }
/* Three across: twelve tiles is four clean rows of three. */
@media (min-width:1020px) { .c-cat-grid { grid-template-columns:repeat(3,1fr); } }
/* A bundle is a different kind of thing from a report and it leads the grid,
   so it says so — with the border only. Tinting the whole card var(--gold-bg)
   also hid the disc behind its covers, which is the same colour: two of the
   twelve cards came out with no circle at all. */
.c-cat-b > .c-rp { border-color:var(--gold); }

/* ── report card ──────────────────────────────────────── */
/* ── A REAL HOLE, NOT A CIRCLE THE COLOUR OF THE BACKGROUND ──
   The first version painted two discs filled with the page ground and gave
   them a border. On a white card over a cream page that is a faint disc with
   a complete ring drawn round it — a bubble sitting on the card, not a bite
   taken out of it. Two cards side by side made it obvious: four rings in a
   row where there should have been two notches.
   So the card carries no background and no border of its own; each half
   carries its own, and each masks a semicircle out of the edge it meets the
   other on. The two semicircles together are the notch, the mask cuts the
   border with them the way a perforation cuts paper, and nothing has to know
   what colour is behind the card. --rp-behind is gone with the discs.
   THE DASHED RULE IS THE TOP HALF'S BOTTOM BORDER, so the same mask that
   cuts the notches cuts the rule short of them for free. */
.c-rp { position:relative; display:flex; flex-direction:column;
        align-items:center; text-align:center; padding:0;
        border:0; background:none; }
.c-rp-top, .c-rp-pay { --notch:11px; --ring:12px;
        -webkit-mask-composite:source-in; mask-composite:intersect; }
.c-rp-top { display:flex; flex-direction:column; align-items:center;
        width:100%; flex:1; padding:22px 18px 15px;
        /* THE ARC'S OWN EDGE, PAINTED JUST OUTSIDE THE CUT. The mask takes
           the border away with everything else inside the notch, which left
           two bites with no line round them — a card that looks torn rather
           than perforated. A stroke cannot be added by a mask (a mask only
           removes), so it is painted: a radial gradient that is transparent
           out to --notch, one pixel of --line from there to --ring, and
           transparent again. It sits a pixel PROUD of the hole, which is the
           whole trick — inside it and the mask would eat it too. */
        background:
          radial-gradient(circle at 0 100%, #0000 0 var(--notch),
            var(--line) var(--notch) var(--ring), #0000 var(--ring)),
          radial-gradient(circle at 100% 100%, #0000 0 var(--notch),
            var(--line) var(--notch) var(--ring), #0000 var(--ring)),
          var(--surface);
        border:1px solid var(--line); border-bottom:1.5px dashed var(--line);
        border-radius:var(--r-card) var(--r-card) 0 0;
        -webkit-mask-image:
          radial-gradient(circle var(--notch) at 0 100%,#0000 97%,#000 100%),
          radial-gradient(circle var(--notch) at 100% 100%,#0000 97%,#000 100%);
        mask-image:
          radial-gradient(circle var(--notch) at 0 100%,#0000 97%,#000 100%),
          radial-gradient(circle var(--notch) at 100% 100%,#0000 97%,#000 100%); }

/* WITHOUT mask-composite there is no way to punch two holes with two
   gradients — the last one would simply win and the left notch would take
   the whole card with it. Support is Chrome 120, Safari 15.4, Firefox 53;
   anything older gets a plain divided ticket with square shoulders, which is
   a card missing an ornament rather than a card missing its left half. */
@supports not ((mask-composite:intersect) or (-webkit-mask-composite:source-in)) {
  .c-rp-top, .c-rp-pay { -webkit-mask-image:none; mask-image:none; }
}
.c-rp.c-on > .c-rp-top, .c-rp.c-on > .c-rp-pay { border-color:var(--gold); }
.c-rp.c-on > .c-rp-top { border-bottom-color:var(--gold-lt); }
/* The admin's corner tag. Gold-solid because that pair is the one already
   measured against both grounds — cream on dark gold in light, near-black on
   light gold in dark — and this sits on the card, not on the page.
   nowrap and a max-width: the server caps the text at 24 characters, and
   this is what stops a card being made taller than the eleven beside it if
   that cap is ever raised. */
/* ── the corner ribbon ──
   A square that clips to the card's own top-left radius, and a bar rotated
   through it. pointer-events:none so it never stands between a reader and
   the card underneath — it is a label, not a control.
   THE TEXT FITS BECAUSE THE SERVER MAKES IT FIT, and the number came off the
   rendered card: at 14 the specimen showed "LIMITED TIME L" running off the
   end of the banner while "LAUNCH OFFER" sat clean inside it. The cap is 12,
   which is exactly "Launch offer" — "Bestseller" is 10, "Ends Sunday" 11.
   A ribbon that trails off mid-word is worse than no ribbon. */
.c-rp-tag { position:absolute; top:0; left:0; width:104px; height:104px;
        overflow:hidden; z-index:3; pointer-events:none;
        border-radius:var(--r-card) 0 0 0; }
.c-rp-tag[hidden] { display:none; }
.c-rp-tag i { position:absolute; top:21px; left:-36px; width:152px;
        display:block; text-align:center; padding:4px 0;
        transform:rotate(-45deg); font-style:normal;
        font-family:var(--font-mono); font-size:9px; letter-spacing:.09em;
        text-transform:uppercase; font-weight:600; white-space:nowrap;
        background:var(--gold-solid); color:var(--gold-solid-text);
        box-shadow:0 2px 7px rgba(0,0,0,.28); }
/* The round well the app puts its cover art in. */
.c-rp-well { display:grid; place-items:center; width:108px; height:108px;
        border-radius:50%; background:var(--gold-bg); overflow:hidden;
        color:var(--gold-dk); margin-bottom:14px; }
.c-rp-img { width:78px; height:78px; object-fit:contain; display:block;
        margin-top:12px; }
/* A cover, shown as one. 126x178 is 1.6x the area the disc gave it and the
   covers' own 0.707 portrait ratio, so nothing is cropped — the widest of
   them is 520x737 and loses half a pixel. The shadow is doing real work:
   these are near-black artwork on a cream card, and without it the cover
   reads as a hole rather than as an object lying on the surface. */
.c-rp-cov { display:block; margin-bottom:14px; line-height:0; }
.c-rp-cov .c-rp-img { width:126px; height:178px; object-fit:cover;
        border-radius:7px; margin-top:0;
        box-shadow:0 8px 22px rgba(0,0,0,.30), 0 1px 3px rgba(0,0,0,.22); }
.c-rp-t { font-size:17px; font-weight:750; letter-spacing:-.01em; }
.c-rp-hi { font-style:normal; font-size:13px; color:var(--ink-3); margin-top:1px; }
/* break-word because the card is 138px wide at two-across on a 390 phone,
   and at that width a description overflowed its clamped box by 3px rather
   than wrapping. Only bites on a word that genuinely cannot fit. */
.c-rp-d { font-size:13px; line-height:1.55; color:var(--ink-2); margin:11px 0 0;
        overflow-wrap:break-word; }
.c-rp-s { list-style:none; margin:12px 0 0; padding:0; display:grid; gap:5px;
        flex:1; text-align:left; width:100%; }
.c-rp-s li { position:relative; padding-left:15px; font-size:12.5px;
        line-height:1.45; color:var(--ink-2); }
.c-rp-s li::before { content:''; position:absolute; left:0; top:7px; width:5px;
        height:5px; border-radius:50%; background:var(--gold-lt); }
/* NOT A FEATURE. It carried a list bullet, so "and 6 more" read as the
   fourth thing in the report rather than as the count of the rest. */
.c-rp-more { color:var(--ink-3) !important; font-style:italic;
             list-style:none; }
.c-rp-more::marker { content:''; }
/* ── THE CARD AT RAIL WIDTH ──
   507px tall inside a horizontal rail: a reader swiping sideways is looking
   at one card that fills most of the screen, which is the shape of a page,
   not of a card in a row. The height is a 108px cover well, a 40px blurb and
   a 106px list of what is inside — three things describing the same product
   before the price.
   The well shrinks and the contents list clamps to three lines. What each
   report contains is what its own page is for; the card's job is to be
   chosen between, and at rail width the reader is comparing them. Written
   after the base rules for the usual reason. */
@media (max-width:640px) {
  /* Retargeted to .c-mq-i, the marquee item: the landing page draws
     these in the shared moving track now rather than in its own
     three-up grid, and every rule below is about a NARROW CARD, not
     about which container it arrived in. Inert on the rashi marquee,
     which has no report card inside it at all. */
  .c-mq-i .c-rp-well { width:76px; height:76px; }
  /* THE ART SCALES WITH THE WELL. The cover is 78px inside a 108px circle
     with a 12px top margin, so shrinking only the circle leaves a 78px image
     in a 76px well that has overflow:hidden — measured: 14px cut off the
     bottom, 82% of the cover showing. Same 72% and 11% ratios as full size.

     NOT what it first looked like. The card appeared to show an empty cream
     disc, which I put down to this clip; it was loading="lazy" and a
     screenshot taken before the image entered the viewport. The clip is real
     and worth fixing, and it is a trimmed edge rather than a missing
     picture — measure the thing before naming it. */
  .c-mq-i .c-rp-img { width:55px; height:55px; margin-top:8px; }
  /* The cover keeps its ratio down here too. Two across on a 390 phone the
     card is 138px wide, so 96px of cover leaves a comfortable margin. */
  .c-mq-i .c-rp-cov { margin-bottom:10px; }
  .c-mq-i .c-rp-cov .c-rp-img { width:96px; height:136px; margin-top:0; }
  /* NO CONTENTS LIST. "Planetary Positions / House Analysis / Ascendant
     Personality / + 6 more" is a table of contents for a document nobody has
     bought yet, and at rail width it is the tallest thing on the card. What
     the report contains is what its own page is for; the card's job is to be
     chosen between. */
  .c-mq-i .c-rp-s { display:none; }
  .c-mq-i .c-rp-d { margin-bottom:2px; }
  /* TWO ACROSS ON A PHONE the card is 138px wide, which is not room for a
     27px price. It stays the largest thing in the block — that is the whole
     point of the block — at a size that fits beside its own struck anchor
     without wrapping. */
  .c-mq-i .c-rp-pr b { font-size:20px; }
  .c-mq-i .c-rp-pr s { font-size:12.5px; }
  .c-mq-i .c-rp-pay { gap:7px; }
  .c-mq-i .c-rp-b { display:flex; align-items:center; justify-content:center; }
}
.c-rp-b { width:100%; text-decoration:none; }

/* ── the foot of every card: offer, price, button ─────────────────────
   ONE BLOCK, PINNED TO THE BOTTOM, so the button sits at the same height on
   every card in a row whatever the description and the contents list did
   above it. This used to be three siblings each with its own margin and a
   `~` rule for every combination of them — six rules to say "these three
   travel together", and they disagreed the moment a fourth thing appeared
   between them. */
/* THE STUB, AND IT IS THE SHORT HALF ON PURPOSE. A ticket stub is a strip,
   not a second card — it carries a number and a way to act on it and nothing
   else. Tighter padding and a smaller figure than the price had when it was
   the bottom of an undivided column: the tear already gives it emphasis, so
   the type does not have to.
   Tinted, so the two halves read as different materials. */
/* ABOVE THE CARD'S OWN HIT OVERLAY, or the buy button cannot be pressed.
   .c-rp-b sets z-index:2 to beat .c-cat-hit's 1 and card_hit's docstring says
   so — but this element carries a MASK for the ticket notches, and a mask
   creates a stacking context. So the button's z-index is scoped INSIDE here
   and never met the overlay at all; the pay half sat at `auto` and the
   overlay painted straight over it.

   The result was a "Get report" button that opened the report instead of the
   checkout: elementFromPoint at the button's own centre returned A.c-cat-hit.
   It reads as working — something happens, and the something is a page about
   the thing you were trying to buy — which is why it survived.

   Only this half is lifted. Everything above the perforation describes the
   document and should open it, which is what the overlay is for; everything
   below it is the transaction. */
.c-rp-pay { position:relative; z-index:2;
        width:100%; flex:none; display:flex; flex-direction:column;
        align-items:center; gap:7px; padding:11px 16px 14px;
        background:
          radial-gradient(circle at 0 0, #0000 0 var(--notch),
            var(--line) var(--notch) var(--ring), #0000 var(--ring)),
          radial-gradient(circle at 100% 0, #0000 0 var(--notch),
            var(--line) var(--notch) var(--ring), #0000 var(--ring)),
          var(--surface-2);
        border:1px solid var(--line); border-top:0;
        border-radius:0 0 var(--r-card) var(--r-card);
        -webkit-mask-image:
          radial-gradient(circle var(--notch) at 0 0,#0000 97%,#000 100%),
          radial-gradient(circle var(--notch) at 100% 0,#0000 97%,#000 100%);
        mask-image:
          radial-gradient(circle var(--notch) at 0 0,#0000 97%,#000 100%),
          radial-gradient(circle var(--notch) at 100% 0,#0000 97%,#000 100%); }
/* ONE SEPARATOR, NOT TWO STACKED. The description carried margin-bottom:14
   and this block carried padding-top:14, so the air between a 12.5px
   description and a 27px price was 28px built out of two rules neither of
   which looked wrong on its own. 14+14 is not a number anybody chose. */

/* ── the price ──
   THE HERO OF THE CARD. Mono because three of these sit side by side and
   the digits have to line up; --gold-dk because it is the brand's on-surface
   gold and it flips with the theme (a dark gold on cream, a light one on
   near-black) rather than being one colour that only works in one. */
.c-rp-pr { display:flex; flex-wrap:wrap; align-items:baseline;
        justify-content:center; gap:9px; width:100%; margin:0;
        font-family:var(--font-mono); }
/* THE FIGURE OWNS ITS LINE, so the anchor and the "Only at" label share
   the one above it and nothing below depends on how many characters the
   price has. Three lines, always, in this order: anchor · label / price /
   badge. */
.c-rp-pr b { flex-basis:100%; font-size:22px; font-weight:750;
        letter-spacing:-.02em; line-height:1.15; color:var(--gold-dk);
        font-variant-numeric:tabular-nums; }
/* "Only at", ahead of the figure. Small, unstruck and NOT mono: it is a
   preposition, not part of the number, and at the row's own weight it read
   as a second price. It sits between the struck anchor and the figure so
   the row is "was this, only at that" in one direction, and reads as a
   plain claim on the reports that carry no anchor at all. */
/* ONE LINE BOX FOR THE PAIR ABOVE THE PRICE, in px rather than by
   inheritance: the struck anchor is 13px and the label 12.5px, so a card
   carrying an anchor made that line 1px taller than a card without one —
   the last of the differences that stopped two cards in a row lining up. */
.c-rp-at { font-family:inherit; font-style:normal; font-size:12.5px;
        line-height:18px; font-weight:600; color:var(--ink-3);
        letter-spacing:0; }
/* The anchor, struck. Quiet on purpose: it is an asking price and the
   figure beside it is what is charged, so the charged one is the loud half.
   `display:none` explicitly — the UA rule for [hidden] is outranked by ANY
   author rule that sets display, and one lands on this element the day
   somebody adds `.c-rp-pr s { display:inline-block }` for the strike. */
.c-rp-pr s { font-size:13px; line-height:18px; color:var(--ink-3);
        text-decoration-thickness:1px; font-variant-numeric:tabular-nums; }
.c-rp-pr s[hidden] { display:none; }

/* ── the launch offer ── */
/* Filled in from the live catalogue, never at build time, and hidden until
   there is one. The anchor is struck and quiet; the saving is the loud half,
   because the saving is the true statement of the two — the struck figure is
   an asking price and the percentage is arithmetic on what is charged. */
/* LEFT, with the list above it. It was centre-right and lined up with
   nothing. And the loudest thing here is the saving, not the price it is
   taken off: 50% off was the smallest text on the card. */
/* THE SAVING, ON THE PRICE'S OWN ROW. It had a line to itself and that line
   was the card's height problem — see the note where the block is built. */
/* ── THE BADGE TAKES A LINE OF ITS OWN, ALWAYS ──────────────────────
   .c-rp-pr wraps, and where it wrapped depended on how wide the numbers
   were. Two bundle cards side by side on a phone, same row, same height:
   "$49.99 Only at / $14.99 / 70% OFF" came out three lines and
   "$19.99 Only at / $6.99 65% OFF" came out two, because $6.99 left just
   enough room beside it for the badge and $14.99 did not. The price
   blocks then measured 85px and 55px, and since the grid gives both
   cards the same height, .c-rp-top absorbed the 30px difference — so
   the two tear lines sat 30px apart and the pair read as lopsided.
   Reported as "both cards do not look symmetric".
   flex-basis:100% takes the decision away from the string: the badge is
   always on its own line, so every card with an offer wraps the same
   way and every card without one is the line it always was. The anchor,
   the label and the price cannot fit on one line together at this width
   for ANY price the catalogue sells, so those two lines are fixed too.
   Fixing it here rather than with a min-height on .c-rp-pay, which
   would have padded every card on the shelf to the tallest one that
   happens to carry an offer today. */
.c-rp-off { font-style:normal; font-family:var(--font-mono); font-size:9.5px;
        letter-spacing:.1em; text-transform:uppercase; padding:3px 8px;
        border-radius:var(--r-pill); background:var(--good-bg);
        color:var(--good); font-weight:600; white-space:nowrap;
        flex-basis:100%; text-align:center; align-self:center;
        margin-inline:auto; width:max-content; }
/* display:none, not visibility:hidden. Nothing is being reserved: the badge
   sits inline on a row the price already gives its height to, so a card with
   an offer and a card without are the same height either way.

   THE OLD NOTE, kept because it is the reasoning that got here.
   AND NOW IT IS display:none AGAIN, FOR A REASON THE FIRST ONE DID NOT HAVE.
   The slot was rendered hidden-but-displayed so a card with no launch offer
   would stand as tall as one with, and a grid row would not jump as the
   answers arrived one by one. That was the right fix while the price and the
   button were laid out from the TOP of the block.

   They are laid out from the bottom now — .c-rp-pay takes the free space
   with margin-top:auto — so the button and the price already line up across
   a row whether the slot is there or not, and reserving it bought nothing
   but 31px of empty card above every undiscounted price. Reported as "there
   is some empty space above the price", measured at 59px total on a card
   with no offer, of which this was more than half.

   THE ROW STILL DOES NOT JUMP, because nothing above the block moves when
   the slot appears: the block grows upward into space the card already had.
   checks/reportcard.mjs asserts the buttons sit at one height, which is the
   thing that actually mattered and is what the reservation was standing in
   for. */
/* ── RESERVED, NOT REMOVED ─────────────────────────────────────────
   display:none here was the last thing making two cards in a row
   different heights: a report with a launch offer carried a badge line
   and the one beside it did not, so their tear lines sat 60px apart.
   The offer also arrives from the live catalogue AFTER first paint, so
   removing the line meant the card changed height under the reader.
   It holds its line either way now and is simply not painted. */
.c-rp-off[hidden] { display:block; visibility:hidden; }
.c-rp-off:empty::before { content:" a0"; }

/* ── report card, brief ───────────────────────────────── */
/* The same card, minus the part that made it tall. The three-section teaser
   and its "and 6 more" were a trailer for a page that now exists and prints
   all nine, and they were four lines of the card's five hundred pixels.
   Two classes on every selector: the base rules are single-class and would
   win on source order for anything declared after this block. */
/* The hover and the featured outline move to the halves, which are the
   things with a border now. */
.c-rp.c-rp-sm { padding:0; }
.c-rp.c-rp-sm .c-rp-top, .c-rp.c-rp-sm .c-rp-pay {
        transition:border-color .15s ease; }
.c-rp.c-rp-sm .c-rp-top { padding:18px 16px 13px; }
@media (prefers-reduced-motion:reduce) {
  .c-rp.c-rp-sm .c-rp-top, .c-rp.c-rp-sm .c-rp-pay { transition:none; }
}
.c-rp.c-rp-sm:hover > .c-rp-top,
.c-rp.c-rp-sm:hover > .c-rp-pay { border-color:var(--gold-lt); }
.c-rp.c-rp-sm .c-rp-s { display:none; }
/* NO CIRCLE. The round well is the app's, and the app's covers are icons;
   ours are book covers with the title set into the artwork, and a disc cut
   them mid-word — "Personalized Kundl", "AREER FINANCE". Contain instead of
   cover only traded that for a 55px-wide thumbnail in a field of air.
   So the cover is shown as what it is: a portrait, whole, at the ratio it
   was drawn (362x512), with the same rounded corner and lift the bundle
   strip gives the same pictures. One family, two sizes. */
/* ── EVERY CARD'S ART BLOCK IS THE SAME HEIGHT ──
   A package draws a fan of covers in a 176px disc; a report draws one cover
   at 104x147. With their margins that is 193px against 161px, and in a grid
   row the shorter card gets the 32px difference — which margin-top:auto then
   collects in ONE place, immediately above the price. Measured on the public
   shelf: eleven cards with 28px between description and price, and
   birth_chart, which happens to sit beside the two packages, with 58px.
   That is the "empty space above the price" as it actually survived.

   The floor is the disc's height, and the art centres in it, so a report
   card spends the difference as air ROUND ITS COVER instead of as a hole
   before its price. Same total height, somewhere it reads as composition.
   Slack measured at 0-2px on every card afterwards. */
.c-rp.c-rp-sm .c-rp-cov, .c-rp.c-rp-sm .c-rp-well {
        min-height:196px; display:flex; align-items:center;
        justify-content:center; margin-bottom:16px; }

/* THE DISC IS BACK, ROUND THE COVER RATHER THAN CROPPING IT. The first one
   was 92px and clipped the artwork mid-word; this is 176 — wide enough that
   a 104x147 portrait sits inside it whole with air at the corners. The
   circle is the ground, the cover is the picture, and neither is doing the
   other's job. Fixed size, not auto: left to size itself the well came out
   12px taller than the bundles' and the two kinds of card sat a
   title-height out of step wherever the grid did not stretch them level. */
.c-rp.c-rp-sm .c-rp-well { display:grid; place-items:center;
        width:196px; height:196px; padding:0; line-height:normal;
        border-radius:50%; overflow:visible;
        /* Room under every disc, not just the ones with dots: the dots are
           out of flow, so without a reserved gap they landed on top of the
           title — and giving the gap only to bundles would make two of the
           twelve cards taller than the rest. */
        margin:0 0 17px; }
/* 124x175, up from 104x147. The artwork is the one thing on this card a
   stranger cannot get anywhere else, and at 104 it was smaller than the
   description under it. The cut widths are 126/252/378, so 124 still draws
   from the 252 file at better than 2x — no new assets and no resampling
   penalty. See fetch/covers.py. */
.c-rp.c-rp-sm .c-rp-img { display:block; width:124px; height:175px;
        max-width:none; object-fit:cover; border-radius:8px;
        box-shadow:0 3px 12px rgba(0,0,0,.34);
        outline:1px solid var(--line); }
/* The nine of ten that have no cover still land in the well, and an icon
   floating in nothing is not a card. It keeps the disc. */
.c-rp.c-rp-sm .c-rp-well:has(svg) { width:124px; height:175px;
        border-radius:8px; background:var(--gold-bg);
        display:grid; place-items:center; }
.c-rp.c-rp-sm .c-rp-t { font-size:16px; }
/* Exactly two lines, whether the sentence fills them or not. Clamped alone,
   a one-line description made its card shorter than the one beside it and
   the row of buttons stopped being a row. */
/* The edition line. Gold rather than --ink-2 so it reads as a property of
   the product and not as the first line of the description under it, and it
   survives the phone rule that hides that description. */
.c-rp-note { display:block; margin:6px 0 0; font-size:11.5px; font-weight:600;
             letter-spacing:.02em; color:var(--gold-dk); }
.c-rp.c-rp-sm .c-rp-note { font-size:11px; margin:5px 0 0; }
.c-rp.c-rp-sm .c-rp-d { font-size:12.5px; line-height:1.55; margin:7px 0 0;
        display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
        overflow:hidden; min-height:calc(2 * 1.55em); }
/* The price rides in the button now, so the button is the bottom of every
   card whatever the description did above it. */
.c-rp.c-rp-sm { display:flex; flex-direction:column; }
/* A VERB AND NOTHING ELSE. The button carried the price for a while and
   needed rules about how to space an action against a number; it says one
   thing now, so it is centred and that is all there is to decide.
   margin-top:auto has gone to .c-rp-pay — the button is no longer the top of
   the bottom block, the offer line is, and only the block's own edge should
   be claiming the free space. */
.c-rp.c-rp-sm .c-rp-b { display:flex; align-items:center;
        justify-content:center; padding:10px 16px; min-height:40px;
        font-size:13.5px; }

/* THE COVERS IN ONE SLOT, IN TURN. Side by side they were honest but they
   made the bundle cards a different shape from the other ten — a 260px strip
   against a 104px cover, so no two cards in a row lined up. Cycling them
   through the same 104x147 slot keeps every card identical and still names
   every report in the bundle, which is the claim. Each one holds for its
   share of the cycle and crossfades into the next; the count is on the
   element because a keyframe cannot divide by a variable. */
.c-rp .c-rp-fan { position:relative; display:block; width:124px;
        height:175px; }
.c-rp .c-rp-fan img { position:absolute; inset:0; display:block;
        width:124px; height:175px; object-fit:cover; border-radius:8px;
        box-shadow:0 3px 12px rgba(0,0,0,.34);
        outline:1px solid var(--line);
        opacity:0; animation-iteration-count:infinite;
        animation-timing-function:ease-in-out; }
@keyframes c-rp-cyc2 {
  0%, 45.0% { opacity:1; }
  50.0%, 95% { opacity:0; }
  100% { opacity:1; }
}
.c-rp-fan[data-n="2"] img,
.c-rp-fan[data-n="2"] ~ .c-rp-dots .c-rp-dot i { animation-name:c-rp-cyc2;
        animation-duration:5.6s; }
@keyframes c-rp-cyc3 {
  0%, 28.3% { opacity:1; }
  33.3%, 95% { opacity:0; }
  100% { opacity:1; }
}
.c-rp-fan[data-n="3"] img,
.c-rp-fan[data-n="3"] ~ .c-rp-dots .c-rp-dot i { animation-name:c-rp-cyc3;
        animation-duration:8.4s; }
@keyframes c-rp-cyc4 {
  0%, 20.0% { opacity:1; }
  25.0%, 95% { opacity:0; }
  100% { opacity:1; }
}
.c-rp-fan[data-n="4"] img,
.c-rp-fan[data-n="4"] ~ .c-rp-dots .c-rp-dot i { animation-name:c-rp-cyc4;
        animation-duration:11.2s; }
@keyframes c-rp-cyc5 {
  0%, 15.0% { opacity:1; }
  20.0%, 95% { opacity:0; }
  100% { opacity:1; }
}
.c-rp-fan[data-n="5"] img,
.c-rp-fan[data-n="5"] ~ .c-rp-dots .c-rp-dot i { animation-name:c-rp-cyc5;
        animation-duration:14.0s; }
@keyframes c-rp-cyc6 {
  0%, 11.7% { opacity:1; }
  16.7%, 95% { opacity:0; }
  100% { opacity:1; }
}
.c-rp-fan[data-n="6"] img,
.c-rp-fan[data-n="6"] ~ .c-rp-dots .c-rp-dot i { animation-name:c-rp-cyc6;
        animation-duration:16.8s; }
/* WHICH OF THEM YOU ARE LOOKING AT. Covers that swap with nothing to count
   them read as one restless picture; five dots say there are five and mark
   the one on show. They ride the same keyframes and the same delays as the
   covers, so the two cannot drift apart. */
.c-rp .c-rp-dots { position:absolute; left:0; right:0; bottom:-13px;
        display:flex; justify-content:center; gap:6px; }
.c-rp .c-rp-dot { position:relative; display:block; width:5px; height:5px;
        border-radius:50%; background:var(--line); }
.c-rp .c-rp-dot i { position:absolute; inset:0; border-radius:50%;
        background:var(--gold); opacity:0;
        animation-iteration-count:infinite;
        animation-timing-function:ease-in-out; }
/* Stationary, not still-but-invisible: reduced motion keeps the first cover
   and its dot, and drops only the turning. */
@media (prefers-reduced-motion:reduce) {
  .c-rp .c-rp-fan img, .c-rp .c-rp-dot i { animation:none; opacity:0; }
  .c-rp .c-rp-fan img:first-child,
  .c-rp .c-rp-dot:first-child i { opacity:1; }
}
/* The bundle's turning covers use the same disc, so the two kinds of card
   are the same shape at a glance. */
/* 196 IS A CEILING, NOT A WIDTH. Fixed, it was 196px of disc inside a card
   inside a page with its own gutters, and on a 360px phone that put its
   right edge at 364 — the whole shelf scrolled sideways by four pixels for
   a card that had no reason to be wider than its column. min() lets it be
   196 wherever there is room and the column wherever there is not; the
   aspect-ratio keeps it a circle rather than an egg once it starts giving
   way. Measured at 320 / 360 / 390 / 430 / 768 / 1024 / 1440. */
.c-rp.c-rp-sm .c-rp-well:has(.c-rp-fan) { position:relative;
        width:min(196px, 100%); aspect-ratio:1; height:auto;
        min-width:0; border-radius:50%; padding:0; overflow:visible; }


/* ── the consultation panel ───────────────────────────── */
/* Everything inside the thread is the simulator's — same classes, same
   keyframes, same scroll — so this block is only the chrome and the sizes a
   browser gives it. Anything that reads `.c-wc .c-sim-*` is a phone number
   being talked out of a phone. */
.c-wc { display:flex; flex-direction:column; min-width:0; overflow:hidden;
        height:clamp(430px, 62vh, 540px);
        border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); }
.c-wc-bar { display:flex; align-items:center; gap:12px; padding:11px 15px;
        border-bottom:1px solid var(--line); }
.c-wc-av { width:38px; height:38px; border-radius:50%; object-fit:cover;
        flex:none; background:var(--surface-2); }
.c-wc-id { flex:1; min-width:0; }
.c-wc-id b { display:block; font-size:15px; font-weight:750; line-height:1.3;
        white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.c-wc-id i { display:flex; align-items:center; gap:5px; font-style:normal;
        font-size:12px; color:var(--ink-3); white-space:nowrap;
        overflow:hidden; text-overflow:ellipsis; }
.c-wc-on { display:inline-flex; align-items:center; gap:5px; flex:none;
        font-style:normal; font-weight:700; color:var(--good); }
.c-wc-on::before { content:''; width:6px; height:6px; border-radius:50%;
        background:var(--good); }
.c-wc-rate { flex:none; font-size:12.5px; font-weight:700; padding:5px 11px;
        border-radius:var(--r-pill); background:var(--gold-bg);
        color:var(--gold-dk); }
/* THE PANEL SCROLLS FOR REAL, and the phone does not.
   The phone's thread is overflow:hidden and rides a transform — a scroller
   with no scrollbar, which is right for a device mock-up nobody can touch.
   Here it was wrong: the conversation arrives over fifteen seconds and until
   the last message lands there is no way to go back and read the one before.
   So the panel gets the browser's own scroller, and WEB_CHAT_JS keeps it
   pinned to the newest message until the reader scrolls away from the bottom
   — the behaviour of every chat client there has ever been.
   No mask at the top: a fade is how you say "there is more above, and you
   cannot get to it". You can get to it now. */
.c-wc .c-sim-thread { padding:16px 16px 4px; overflow-y:auto;
        overscroll-behavior:contain; scrollbar-width:thin;
        -webkit-mask-image:none; mask-image:none; }
.c-wc .c-sim-sc { transform:none; }
.c-wc .c-sim-thread::-webkit-scrollbar { width:10px; }
.c-wc .c-sim-thread::-webkit-scrollbar-thumb {
        border:3px solid transparent; background-clip:content-box;
        border-radius:6px; background-color:var(--line); }
.c-wc .c-sim-thread:hover::-webkit-scrollbar-thumb {
        background-color:var(--ink-3); }
/* A bubble that may run 560px wide does not need to run 84% of a 900px panel
   as well — past about 70 characters a line, a chat reads as a document. */
.c-wc .c-sim-b { max-width:min(560px, 82%); padding:11px 15px 9px;
        font-size:14px; line-height:1.6; }
.c-wc .c-sim-them { border-radius:16px 16px 16px 5px; }
/* Gold FILL on the phone, gold WASH here — /home/chats/ draws the reader's own
   messages as --gold-bg with a --gold-lt edge, and this panel is a preview of
   that screen rather than of the app's. Which means the timestamp inside it is
   no longer on a solid gold ground, so its colour has to come back off
   --gold-solid-text or it is white on cream. */
.c-wc .c-sim-me { border-radius:16px 16px 5px 16px; font-weight:500;
        background:var(--gold-bg); color:var(--ink);
        border:1px solid var(--gold-lt); }
.c-wc .c-sim-me .c-sim-meta { color:var(--gold-dk); }
.c-wc .c-sim-meta { font-size:10.5px; margin:6px 0 0 12px; }
.c-wc .c-sim-row { gap:9px; }
.c-wc .c-sim-row .c-sim-av, .c-wc .c-sim-avx { width:30px; height:30px; }
.c-wc .c-sim-m > .c-sim-row, .c-wc .c-sim-m > .c-sim-b,
.c-wc .c-sim-typing .c-sim-row { margin-bottom:11px; }
.c-wc .c-sim-day span { font-size:10px; }
.c-wc .c-sim-dots { padding:13px 16px; }
.c-wc-comp { display:flex; align-items:center; gap:10px; padding:11px 13px;
        border-top:1px solid var(--line); }
.c-wc-in { flex:1; min-width:0; padding:11px 15px; border-radius:var(--r-pill);
        border:1px solid var(--line); background:var(--surface-2);
        font-size:13.5px; color:var(--ink-3); white-space:nowrap;
        overflow:hidden; text-overflow:ellipsis; }
.c-wc-send { display:inline-flex; align-items:center; gap:7px; flex:none;
        padding:11px 17px; border-radius:var(--r-pill); font-size:13px;
        font-weight:700; background:var(--gold-solid);
        color:var(--gold-solid-text); }
.c-wc-send .c-sim-pl { width:14px; height:14px; }
.c-wc-foot { display:flex; align-items:center; justify-content:center; gap:6px;
        padding:0 14px 12px; font-size:11px; color:var(--ink-3); }
.c-wc-sp { min-width:0; overflow:hidden; text-overflow:ellipsis; }
/* Three dots frozen at the end of a finished conversation is a chat that has
   hung, so the hold hides whichever one is mid-blink. */
.c-wc-held .c-sim-typing { visibility:hidden; }
/* The skip-to-the-end control. Not rendered in the markup: with the script
   blocked it would be a button that does nothing, and the loop is a
   perfectly good fallback on its own. */
/* Sized like /home/chats/'s Close, which is the same thing in the same place:
   a secondary control in a chat header. 12px of padding a side is a 31px
   target, which is a mouse button rather than a thumb one. */
.c-wc-rd { font:inherit; cursor:pointer; flex:none; white-space:nowrap;
        font-size:13px; font-weight:600; padding:9px 14px;
        border-radius:var(--r-pill); border:1px solid var(--line);
        background:var(--surface-2); color:var(--ink-2); }
.c-wc-rd:hover { border-color:var(--gold); color:var(--gold-dk);
        background:var(--gold-bg); }
@media (max-width:560px) {
  .c-wc-rd { padding:9px 12px; font-size:12.5px; }
  /* "Skip" and "Play", which is all a 300px bar has room for. */
  .c-wc-rd-x { display:none; }
}
@media (max-width:560px) {
  .c-wc { height:clamp(400px, 70vh, 470px); }
  .c-wc .c-sim-b { max-width:88%; font-size:13px; padding:10px 13px 8px; }
  .c-wc-in { font-size:12.5px; }
  /* The word goes, the plane stays. */
  .c-wc-send { padding:11px 13px; font-size:0; gap:0; }
  /* "Online · Advanced Vedic Jyo…" — a specialty cut mid-word next to a name
     and a rate is three things in a 300px bar. The name is the one that has
     to survive, and on a phone it does not: the avatar, the rate and the read
     control leave about 100px for a name that wants 140, so "Acharya
     Parashara" arrives as "Acharya …". The rate goes instead. It is on every
     card on this page, in the section's own copy, and in the page CTA — the
     header is the one place it is a repeat. */
  .c-wc-sp, .c-wc-rate { display:none; }
  /* Even without the rate, "Acharya Parashara" wants 145px of the 150 that a
     360px phone leaves after the avatar, the gaps and the read control. A
     point off the name and two off the bar's padding is the difference
     between a name and "Acharya …". */
  .c-wc-bar { gap:10px; padding:10px 12px; }
  .c-wc-id b { font-size:14px; }
}

/* ── simulator ────────────────────────────────────────── */
/* The bezel is 11px a side plus the section's own padding — at 320px that
   came to 26px more than the viewport. width:100% with box-sizing lets it
   shrink instead. */
.c-sim { width:100%; max-width:330px; min-width:0; margin:0 auto;
         box-sizing:border-box;
         border-radius:44px; padding:11px; background:#1B1B1F;
         box-shadow:0 26px 70px rgba(0,0,0,.30), inset 0 0 0 2px #34343A; }
/* A real phone is about 19.5:9, so the body takes an aspect ratio rather than
   a fixed height — at 308px of screen that is 667px tall, which is a phone.
   It used to size to its content instead, because the thread had no way to
   scroll and a fixed frame cut the last messages off under the composer. It
   scrolls now, so the frame can be a phone at every width. */
.c-sim-body { position:relative; border-radius:34px; overflow:hidden;
         background:var(--surface); display:flex; flex-direction:column;
         aspect-ratio:9 / 19.5; min-height:520px; }
.c-sim-bar { position:relative; display:flex; align-items:center;
         justify-content:space-between; padding:9px 20px 4px;
         background:var(--gold-solid); color:var(--gold-solid-text);
         font-size:12px; font-weight:700; }
.c-sim-isl { position:absolute; top:7px; left:50%; transform:translateX(-50%);
         width:78px; height:20px; border-radius:12px; background:#000; }
.c-sim-icons { display:flex; align-items:center; gap:4px; }
.c-sim-g { height:9px; width:auto; fill:currentColor; }
.c-sim-head { display:flex; align-items:center; gap:8px; padding:4px 14px 11px;
         background:var(--gold-solid); color:var(--gold-solid-text); }
.c-sim-head b { min-width:0; font-size:14.5px; font-weight:750;
         white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Name over a status line, which is where every chat app puts "online" and
   where this one can put the thing that actually matters here: the rate. */
.c-sim-who { flex:1; min-width:0; display:flex; flex-direction:column;
         line-height:1.25; }
.c-sim-who b { display:block; }
.c-sim-who i { display:block; font-style:normal; font-size:9.5px;
         font-weight:600; letter-spacing:.01em; }
.c-sim-back { display:grid; place-items:center; width:20px; height:20px; }
.c-sim-back svg { width:16px; height:16px; fill:none; stroke:currentColor;
         stroke-width:2.2; }
.c-sim-av { width:26px; height:26px; border-radius:50%; object-fit:cover;
         flex:none; background:var(--surface-2); }
/* Opaque, and the inverse of the bar it sits on. A translucent white pill
   composites over gold, so its real contrast cannot be measured — the checker
   read the rgba directly and got 1.05:1. These two tokens are chosen to clear
   4.5:1 against each other in both themes, so swapping them is safe by
   construction. */
.c-sim-wal { font-size:10.5px; font-weight:700; padding:3px 8px;
         border-radius:var(--r-pill); background:var(--gold-solid-text);
         color:var(--gold-solid); }
/* THE THREAD SCROLLS, AND IT DOES IT WITHOUT A LINE OF SCRIPT.
   The conversation has to start at the top of an empty screen, fill downward,
   and then — once it is taller than the screen — ride up so the newest message
   stays above the composer. Those are two different alignments, and no
   flexbox keyword gives both: flex-start never scrolls, flex-end parks the
   opening greeting at the bottom of an empty phone.
   What gives both is the offset a scroll container would apply:

       min(0, viewport height − content height)

   zero while the conversation fits, negative by exactly the overflow once it
   does not. Both terms are expressible. Making the thread a size container
   turns its own height into 100cqh, and a percentage in translateY already
   resolves against the moving element's own height — so the scroller can ask
   for the difference between the two without either being written down as a
   number. And because a message opens its height over ~0.4s rather than
   appearing, the content height changes continuously, so the scroll that
   follows it is smooth rather than a jump per message.
   The mask fades whatever is riding off the top, so the cut reads as
   scrollback instead of as a bug; there is nothing in that band to fade until
   the thread actually fills. */
/* --rail, not --surface-2. A chat needs its bubbles to read as objects on a
   ground, and --surface on --surface-2 measured 1.05:1 in dark — no bubble at
   all, just text on a flat panel. The fill difference in a real chat app is
   small too, so the separation is carried by a visible border and a shadow
   rather than by the fill alone. */
.c-sim-thread { flex:1; min-height:0; overflow:hidden; padding:12px 12px 4px;
         background:var(--rail); container-type:size;
         -webkit-mask-image:linear-gradient(to bottom, transparent 0,
                            #000 26px, #000 100%);
         mask-image:linear-gradient(to bottom, transparent 0,
                    #000 26px, #000 100%); }
.c-sim-sc { display:flex; flex-direction:column; justify-content:flex-start;
         transform:translateY(min(0px, calc(100cqh - 100%))); }
/* No container queries, no cqh — the calc is invalid and the whole transform
   is dropped, which leaves the thread pinned to the top and the tail of the
   conversation under the composer. Pin it to the bottom there instead: the
   newest message is the one worth keeping. */
@supports not (height:100cqh) {
  .c-sim-thread { display:flex; flex-direction:column;
         justify-content:flex-end; }
}
/* flex:none FIRST, because everything else here depends on it. The thread is a
   flex column, so its children are flex items with flex-shrink:1 by default —
   and once the messages together are taller than the screen, the browser
   quietly squeezes every one of them instead of overflowing. That is not a
   visible squeeze: the bubble keeps its text and loses its height, so what
   actually comes out the bottom is the timestamp, sitting up to 18px below the
   bubble it belongs to. It reads as a broken timestamp and it is nothing of
   the sort. Messages keep their height and the thread overflows, which is what
   overflow:hidden is there for.
   The wrapper is also what opens: it clips its own child while the height runs
   from zero, so the spacing has to sit inside it or a collapsed message would
   still leave its margin behind. */
.c-sim-m, .c-sim-typing { flex:none; }
.c-sim-m { overflow:hidden; }
.c-sim-m > .c-sim-row, .c-sim-m > .c-sim-b { margin-bottom:7px; }
.c-sim-mine { display:flex; justify-content:flex-end; }
.c-sim-row { display:flex; align-items:flex-end; gap:6px; }
.c-sim-row .c-sim-av { width:22px; height:22px; }
/* Holds the gutter for a reply that follows another reply, so the run lines
   up under the face instead of stepping left. */
.c-sim-avx { background:none; }
/* flow-root so the bubble CONTAINS the floated time rather than merely
   clearing it, which is what lets the time tuck onto the end of the last line
   the way every chat app does it. Stacking it on its own line instead cost
   ~11px a message, and nine of those is a fifth of the screen. */
.c-sim-b { display:flow-root; max-width:84%; padding:8px 10px 6px;
         font-size:11.5px; line-height:1.5; }
.c-sim-them { border-radius:13px 13px 13px 3px; background:var(--surface);
         color:var(--ink); border:1px solid var(--line);
         box-shadow:0 1px 3px rgba(0,0,0,.10); }
.c-sim-me { border-radius:13px 13px 3px 13px;
         background:var(--gold-solid); color:var(--gold-solid-text);
         font-weight:600; }
.c-sim-b b { font-weight:750; }
.c-sim-b em { font-style:italic; }
/* Inside the bubble, bottom right, floated so it tucks onto the end of the
   last line when there is room, with the ticks beside it — the arrangement
   this audience has read ten thousand times. */
.c-sim-meta { float:right; display:flex; align-items:center; gap:3px;
         margin:5px 0 0 10px; font-size:8.5px; font-weight:600;
         line-height:1; white-space:nowrap; color:var(--ink-3); }
.c-sim-me .c-sim-meta { color:var(--gold-solid-text); }
.c-sim-tk { width:12px; height:8px; flex:none; }
/* The day divider. Same chip every chat app draws. */
.c-sim-day { display:flex; justify-content:center; margin-bottom:9px; }
.c-sim-day span { padding:3px 10px; border-radius:var(--r-pill);
         background:var(--surface); border:1px solid var(--line);
         font-size:8.5px; font-weight:700; letter-spacing:.06em;
         text-transform:uppercase; color:var(--ink-3); }
/* the three dots while the astrologer is writing */
.c-sim-dots { display:flex; align-items:center; gap:4px; padding:11px 13px; }
.c-sim-dots i { width:5px; height:5px; border-radius:50%; background:var(--ink-3);
         animation:c-sim-dot 1.2s ease-in-out infinite; }
.c-sim-dots i:nth-child(2) { animation-delay:.18s; }
.c-sim-dots i:nth-child(3) { animation-delay:.36s; }
.c-sim-typing { overflow:hidden; }
.c-sim-typing .c-sim-row { margin-bottom:7px; }
@keyframes c-sim-dot { 0%,60%,100% { opacity:.28; transform:translateY(0); }
                       30% { opacity:1; transform:translateY(-3px); } }
/* Paused on hover so anyone reading is not interrupted, and stopped entirely
   under reduced motion — where the whole thread simply shows, bottom-anchored,
   so what you see is the end of the conversation rather than the start. */
.c-sim:hover .c-sim-m, .c-sim:hover .c-sim-typing { animation-play-state:paused; }
@media (prefers-reduced-motion:reduce) {
  .c-sim-m, .c-sim-typing, .c-sim-dots i { animation:none !important; opacity:1;
         transform:none; max-height:none; }
  .c-sim-typing { display:none; }
}
.c-sim-comp { display:flex; align-items:center; gap:8px; margin:0 10px 6px;
         padding:7px 9px; border-radius:var(--r-pill); background:var(--surface);
         border:1px solid var(--line); }
/* The clip is not in a filled circle: an attach control that looks like a
   second button competes with the one button that matters. */
.c-sim-cs { display:grid; place-items:center; width:20px; height:20px;
         flex:none; color:var(--ink-3); }
.c-sim-cl { width:17px; height:17px; }
.c-sim-ph { flex:1; min-width:0; font-size:11px; color:var(--ink-3);
         white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.c-sim-send { display:grid; place-items:center; width:26px; height:26px; flex:none;
         border-radius:50%; background:var(--gold-solid);
         color:var(--gold-solid-text); }
.c-sim-pl { width:14px; height:14px; }
.c-sim-foot { display:flex; align-items:center; justify-content:center; gap:5px;
         padding:0 10px 10px; font-size:8.5px; color:var(--ink-3); }
.c-sim-home { position:absolute; bottom:6px; left:50%; transform:translateX(-50%);
         width:110px; height:4px; border-radius:3px; background:var(--ink);
         opacity:.28; }

/* ── store badges ─────────────────────────────────────── */
.c-st-g { display:flex; flex-wrap:wrap; gap:10px; }
.c-st-b { display:inline-flex; align-items:center; gap:10px; min-height:52px;
          padding:0 16px 0 14px; border-radius:11px; text-decoration:none;
          background:var(--pill-ink); color:var(--pill-text);
          border:1px solid var(--pill-ink); }
.c-st-b:hover { filter:brightness(1.14); }
/* Not yet on either store. Outlined rather than filled, and no hover lift, so
   it reads as a notice and not as a button somebody forgot to wire up. The
   pill-ink border keeps it the same shape and weight as the live badge, which
   is what it becomes the day the listings go up. */
.c-st-soon { background:transparent; color:var(--ink-2);
             border-color:var(--line); cursor:default; }
.c-st-soon:hover { filter:none; }
.c-st-soon .c-st-i { color:var(--ink-2); }
.c-st-soon b { color:var(--ink); }
.c-st-i { display:grid; place-items:center; width:24px; height:24px; flex:none; }
.c-st-i svg { width:22px; height:22px; }
.c-st-b i { display:block; font-style:normal; font-size:10px; letter-spacing:.03em;
            opacity:.8; }
.c-st-b b { display:block; font-size:15px; font-weight:700; line-height:1.15;
            margin-top:1px; }

/* ── phone frame ──────────────────────────────────────── */
/* EVERY DIMENSION IN HERE SCALES WITH THE FRAME'S OWN WIDTH.
   It was 9px of bezel and a 38px radius, which are the right numbers at the
   290px this was drawn at and nowhere else: put three of these side by side
   at 167px and the bezel is half again as thick in proportion, which is what
   a cheap phone case looks like. cqi is a percentage of the container's
   inline size, so 2.6cqi is 7.5px at 290 and 4.3px at 167 — the same phone,
   smaller. A percentage would not do for the radius: percentage radii resolve
   per axis, and on a 9:19.5 box that gives corners shaped like commas. */
.c-ph { width:100%; max-width:290px; margin:0 auto; container-type:inline-size; }
.c-ph-s { position:relative; border-radius:11cqi; padding:2.6cqi;
          background:var(--pill-ink);
          box-shadow:0 22px 60px rgba(0,0,0,.24), 0 0 0 1px var(--line) inset; }
.c-ph-n { position:absolute; top:4.6cqi; left:50%; transform:translateX(-50%);
          width:30cqi; height:7.4cqi; border-radius:4cqi;
          background:var(--pill-ink); z-index:2; }
/* height:auto is not tidiness. img() writes width and height attributes, which
   are presentational hints — width:100% overrides one of them and the other
   stays, so the first real screenshot put through this frame rendered 167px
   wide and 913px tall. */
.c-ph-s img { display:block; width:100%; height:auto; border-radius:8.6cqi; }

/* ── live demo ────────────────────────────────────────── */
/* minmax(0,1fr), NOT the default auto track. Under 900px the picker is a
   sideways scroller, and an auto track sizes to ITS max-content — eight cards
   laid end to end, 716px of grid inside a 390px phone, with the chat hanging
   off the right of the screen. The same mistake /home/chats/'s reader was
   fixed for; a scroller inside a grid needs the track told it may be smaller
   than its contents. */
.c-dm { display:grid; grid-template-columns:minmax(0,1fr); gap:14px; }
@media (min-width:900px) { .c-dm { grid-template-columns:290px minmax(0,1fr); gap:20px; } }
.c-dm-list { align-content:start; padding:12px; min-width:0;
        border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); }
.c-dm-list .c-label { display:block; padding:2px 4px 8px; }
.c-dm-picks { display:grid; gap:5px; }
.c-dm-p { display:flex; align-items:center; gap:10px; padding:8px 10px;
        min-height:var(--tap-min); cursor:pointer; border-radius:var(--r-in);
        border:1px solid transparent; }
.c-dm-p:hover { background:var(--surface-2); }
.c-dm-ph { width:36px; height:36px; border-radius:50%; object-fit:cover; flex:none;
        background:var(--surface-2); }
.c-dm-p .c-avatar { width:36px; height:36px; border-radius:50%; font-size:14px; }
.c-dm-p b { display:block; font-size:13.5px; font-weight:700; }
.c-dm-p i { display:block; font-style:normal; font-size:11.5px; color:var(--ink-3); }
/* Under 900px the list is above the chat rather than beside it, and eight
   rows of it is 400px of names before a reader reaches the thing the section
   is for. Sideways, it is one row — the same shape as the filter chips
   further up the page, and it keeps all eight reachable. The specialty goes
   with the wrap: it is the widest thing in the card and it is repeated in the
   panel's own header two inches below.
   AFTER the rules it overrides, not before. Written above them it lost to
   `.c-dm-p i { display:block }` on source order at equal specificity — the
   media query is not a tie-break. */
@media (max-width:899px) {
  .c-dm-picks { display:flex; gap:8px; overflow-x:auto; padding-bottom:4px;
        scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch; }
  .c-dm-p { flex:0 0 auto; scroll-snap-align:start; border-color:var(--line); }
  .c-dm-p i { display:none; }
}
/* Every pane shares one grid cell so the stage is as tall as the tallest and
   switching astrologer cannot move the page. */
.c-dm-stage { display:grid; min-width:0; }
.c-dm-pane { grid-area:1 / 1; min-width:0; visibility:hidden; opacity:0;
        pointer-events:none; transition:opacity .16s; padding:18px;
        border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); }
/* The pane is the chat's frame, so it does not draw a second one around it. */
.c-dm-chips { display:flex; flex-wrap:wrap; align-items:center; gap:5px;
        margin-bottom:12px; }
.c-dm-m { font-size:11.5px; font-weight:600; padding:4px 9px; border-radius:var(--r-sm);
        color:var(--ink-2); background:var(--surface-2); }
/* The languages are not a pill. They are not a system you can pick — they are
   a fact about the same person, and giving them the same chip makes "Hindi"
   read as a fourth thing they practise. */
.c-dm-l { font-size:11.5px; color:var(--ink-3); padding-left:4px; }
@media (prefers-reduced-motion:reduce) { .c-dm-pane { transition:none; } }

/* ── blog posts ───────────────────────────────────────── */
.c-po { display:flex; flex-direction:column; text-decoration:none;
        color:var(--ink); background:var(--surface); overflow:hidden;
        border:1px solid var(--line); border-radius:var(--r-card);
        transition:border-color .15s ease, transform .15s ease; }
.c-po:hover { border-color:var(--gold); transform:translateY(-2px); }
/* aspect-ratio on the wrapper, not the image, so a drawn SVG hero and a
   photograph reserve exactly the same box and the row cannot reflow when one
   of the six is replaced. */
.c-po-i { display:block; aspect-ratio:16 / 9; overflow:hidden;
          border-bottom:1px solid var(--line-soft); background:var(--rail); }
.c-po-i img, .c-po-i svg { display:block; width:100%; height:100%;
          object-fit:cover; transition:transform .4s ease; }
.c-po:hover .c-po-i img, .c-po:hover .c-po-i svg { transform:scale(1.035); }
.c-po-x { display:flex; flex-direction:column; flex:1; padding:18px 20px 20px; }
.c-po-dt { color:var(--ink-3); }
.c-po-dt::before { content:'\00B7'; margin-right:8px; }
.c-po-c { display:flex; align-items:center; gap:8px; font-family:var(--font-mono);
          font-size:10px; letter-spacing:.13em; text-transform:uppercase;
          color:var(--gold-dk); margin-bottom:8px; }
.c-po-t { font-family:var(--font-serif); font-size:19px; line-height:1.24;
          letter-spacing:-.014em; font-weight:600; margin:0 0 9px;
          text-wrap:balance; }
.c-po-d { font-size:14px; line-height:1.55; color:var(--ink-2); margin:0 0 16px; }
.c-po-m { display:flex; align-items:center; gap:6px; margin-top:auto;
          font-family:var(--font-mono); font-size:11px; color:var(--ink-3); }
.c-po:hover .c-po-m { color:var(--gold-dk); }
.c-po-s { font-family:var(--font-mono); font-size:9px; letter-spacing:.1em;
          text-transform:uppercase; color:var(--ink-3); padding:2px 6px;
          border:1px solid var(--line); border-radius:var(--r-pill); }
@media (prefers-reduced-motion:reduce) {
  .c-po, .c-po-i img, .c-po-i svg { transition:none; }
  .c-po:hover .c-po-i img, .c-po:hover .c-po-i svg { transform:none; }
}

/* ── quotes ───────────────────────────────────────────── */
.c-qt { display:flex; flex-direction:column; gap:16px; margin:0; padding:22px;
        background:var(--surface); border:1px solid var(--line);
        border-radius:var(--r-card); }
/* The open quote is set in the serif and hung, so the first line of the quote
   starts on the same left edge as everything else in the card. */
.c-qt-b { position:relative; margin:0; font-size:15px; line-height:1.62;
          color:var(--ink); }
.c-qt-b::before { content:'\201C'; position:absolute; left:-2px; top:-16px;
          font-family:var(--font-serif); font-size:44px; line-height:1;
          color:var(--gold-lt); pointer-events:none; }
.c-qt-f { display:flex; align-items:center; gap:10px; margin-top:auto;
          padding-top:15px; border-top:1px solid var(--line-soft); }
.c-qt-k { flex:none; display:grid; place-items:center; width:32px; height:32px;
          border-radius:50%; background:var(--gold-bg); color:var(--gold-dk); }
.c-qt-x { min-width:0; display:flex; flex-direction:column; line-height:1.35; }
.c-qt-x b { font-size:13.5px; font-weight:650; }
/* The city is the same line as the name but not the same weight — one is who,
   the other is where, and bolding both makes the pair read as a full name. */
.c-qt-x b span { font-weight:400; color:var(--ink-2); }
.c-qt-x i { font-style:normal; font-size:12px; color:var(--ink-3); }
/* A drawn initial, not a face. The disc already existed for an icon, so this
   is the same 32px circle with a letter in it. */
.c-qt-k { font-family:var(--font-serif); font-size:15px; font-weight:600;
          line-height:1; }
/* The astrologer, at the far end and smaller than the monogram — a face the
   reader is told the name of, rather than one standing in for a name. */
.c-qt-a { flex:none; margin-left:auto; width:26px; height:26px; overflow:hidden;
          border-radius:50%; background:var(--rail);
          border:1px solid var(--line-soft); }
.c-qt-a img { width:100%; height:100%; object-fit:cover; display:block; }

/* ── the quote marquee ────────────────────────────────── */
/* Full-bleed by cancelling .l-wrap's 16px padding rather than by 100vw: a
   100vw child is wider than the page whenever a scrollbar is showing, and
   that is a sideways scroll on every screen. -16px is exactly the padding, so
   the row reaches the viewport edge below 1180px and stops at the content
   edge above it. */
.c-qmq { position:relative; overflow:hidden; margin-inline:-16px;
         padding-block:4px;
         -webkit-mask-image:linear-gradient(90deg, transparent, #000 5%,
                            #000 95%, transparent);
         mask-image:linear-gradient(90deg, transparent, #000 5%,
                    #000 95%, transparent); }
.c-qmq-t { display:flex; width:max-content; align-items:stretch;
           animation:c-qmq-run 84s linear infinite; }
.c-qmq-c { display:contents; }
/* A row that never stops is a row you cannot finish reading. Hover stops it
   for a mouse, focus-within for a keyboard. */
.c-qmq:hover .c-qmq-t, .c-qmq:focus-within .c-qmq-t { animation-play-state:paused; }
.c-qmq .c-qt { flex:0 0 clamp(258px, 76vw, 340px); margin-right:14px; }
@keyframes c-qmq-run { to { transform:translate3d(-50%, 0, 0); } }
/* Stationary means SWIPEABLE, not frozen. The clone leaves the DOM so the row
   is twelve cards long and ends where the twelfth ends. */
/* A RAIL ON A PHONE TOO, not only for prefers-reduced-motion. The same
   argument as the question ticker: a mask that fades a card out at each
   edge reads as motion on a wide screen and as a cut-off card on a 390px
   one, where the band is barely one card wide. And it is the fourth
   horizontal thing on this page — three of them swipe, so this one should
   too rather than being the odd one that moves on its own. */
@media (max-width:640px), (prefers-reduced-motion:reduce) {
  .c-qmq { overflow-x:auto; scroll-snap-type:x proximity;
           padding-inline:16px; margin-inline:-16px;
           scroll-padding-inline:16px; scrollbar-width:none;
           -webkit-mask-image:none; mask-image:none; }
  .c-qmq::-webkit-scrollbar { display:none; }
  .c-qmq-t { animation:none; }
  .c-qmq-c[aria-hidden] { display:none; }
  .c-qmq .c-qt { scroll-snap-align:start; }
}

/* ── score scale ──────────────────────────────────────── */
.c-scl { border:1px solid var(--line); border-radius:var(--r-card);
        background:var(--surface); padding:16px 14px 12px; }
.c-scl-t { font-family:var(--font-mono); font-size:10px; letter-spacing:.12em;
          text-transform:uppercase; color:var(--ink-3); margin-bottom:14px;
          padding:0 4px; }
.c-scl-g { display:flex; align-items:flex-end; gap:4px; height:150px;
          padding-top:18px; }
.c-scl-c { position:relative; flex:1 1 0; min-width:0; display:flex;
          flex-direction:column; align-items:center; justify-content:flex-end;
          height:100%; gap:5px; }
/* Absolute and nowrap so the caption is readable over a narrow column. The
   first and last columns anchor to their own edge instead of centring, or the
   caption hangs off the side of the card. */
.c-scl-tag { position:absolute; bottom:100%; left:50%; transform:translateX(-50%);
          margin-bottom:2px; font-family:var(--font-mono); font-size:8.5px;
          letter-spacing:.12em; text-transform:uppercase; white-space:nowrap;
          color:var(--ink-3); }
.c-scl-c:first-child .c-scl-tag { left:0; transform:none; }
.c-scl-c:last-child .c-scl-tag { left:auto; right:0; transform:none; }
.c-scl-c.c-on .c-scl-tag { color:var(--gold-dk); }
.c-scl-v { font-family:var(--font-mono); font-size:10.5px; color:var(--ink-3);
          font-variant-numeric:tabular-nums; }
.c-scl-b { width:100%; height:var(--h); border-radius:5px 5px 3px 3px;
          background:var(--gold-lt); }
.c-scl-l { font-family:var(--font-mono); font-size:9.5px; color:var(--ink-3);
          white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
          max-width:100%; }
/* Every fifth day number, and the last one, so the axis still says where it
   ends. visibility rather than display, so the columns keep their widths and
   the bars stay where the data put them. */
@media (max-width:640px) {
  .c-scl-dense .c-scl-c:not(:nth-child(5n+1)):not(:last-child) .c-scl-l {
    visibility:hidden; }
  /* The ones that stay need to be wider than their own column — a day in a
     month is 7px and "01" is 13 — and they can be, because the four columns
     either side of each are now blank. Centred on the column it labels so it
     still points at the right bar. */
  .c-scl-dense .c-scl-l { overflow:visible; max-width:none; }
  .c-scl-dense .c-scl-c { position:relative; }
  .c-scl-dense .c-scl-c .c-scl-l { position:absolute; top:100%; left:50%;
    transform:translateX(-50%); }
  .c-scl-dense .c-scl-g { padding-bottom:14px; }
}
/* The same treatment for a middling row, but only where it is actually too
   tight — twelve months read fine at 390. */
@media (max-width:379px) {
  .c-scl-mid .c-scl-c:not(:nth-child(2n+1)):not(:last-child) .c-scl-l {
    visibility:hidden; }
  .c-scl-mid .c-scl-l { overflow:visible; max-width:none; }
  .c-scl-mid .c-scl-c { position:relative; }
  .c-scl-mid .c-scl-c .c-scl-l { position:absolute; top:100%; left:50%;
    transform:translateX(-50%); }
  .c-scl-mid .c-scl-g { padding-bottom:14px; }
}
/* The named best and worst come back from the engine. A column chart that does
   not say which one is the good one is decoration. */
.c-scl-c.c-on .c-scl-b { background:var(--gold-solid); }
.c-scl-c.c-on .c-scl-v, .c-scl-c.c-on .c-scl-l { color:var(--gold-dk);
          font-weight:650; }
/* The hardest one is drawn hollow rather than in a second colour: a red bar in
   a gold chart reads as an error, and this is a quiet week, not a failure. */
.c-scl-c.c-lo .c-scl-b { background:var(--surface-2);
          border:1.5px dashed var(--line); }
.c-scl-c.c-lo .c-scl-v, .c-scl-c.c-lo .c-scl-l { color:var(--ink-3);
          font-weight:650; }

/* ── link bar ─────────────────────────────────────────── */
.c-lb { display:flex; flex-wrap:wrap; gap:7px; }
/* Centred, for the one place this is a navigation strip directly under a
   centred page head rather than a row of related links inside a section.
   The panchang's seven days sat hard left under a head that had just been
   centred, so the page had two different left edges in its first 200px.
   A modifier and not the default: everywhere else this bar sits under a
   left-aligned section heading, and centring it there would make the same
   mistake in the other direction. */
.c-lb-c { justify-content:center; }
/* ── ONE ROW THAT SCROLLS, WHERE THE ITEMS ARE A SEQUENCE ──
   .c-lb wraps, which is right for a bag of links that happen to be related.
   It is wrong for a run of consecutive DAYS: wrapped, the week breaks across
   two lines at an arbitrary point and the reader has to reassemble the order
   — and on a phone the second line is two items long and reads as a
   different control. Opt-in, because most link bars are not sequences.
   Centring is dropped while scrolling: a centred flex row with overflow
   hides the overflowing start, which would put the first day out of reach. */
@media (max-width:640px) {
  .c-lb-seq { flex-wrap:nowrap; overflow-x:auto; justify-content:flex-start;
              scrollbar-width:none; overscroll-behavior-x:contain;
              -webkit-overflow-scrolling:touch; }
  .c-lb-seq::-webkit-scrollbar { display:none; }
  /* A FLOOR, WHICH IS ALSO THE DECLARATION. Seven pills sized by their own
     words come out ragged — "Fri" narrower than "Wed", "1 Aug" than "19 Aug"
     — and a row a thumb sweeps across should have stops it can predict. The
     floor is also what says the row is wide ON PURPOSE: shell-mobile flags a
     sideways scroller inside the page unless something in it declares a
     min-width, which is exactly the difference between a strip built to
     scroll and a layout that burst its box. */
  .c-lb-seq > * { flex:0 0 auto; min-width:72px; }
}
.c-lb-i { display:flex; flex-direction:column; justify-content:center;
          min-height:44px; padding:5px 15px; border-radius:var(--r-pill);
          border:1px solid var(--line); background:var(--surface);
          font-size:14px; font-weight:650; color:var(--ink-2);
          text-decoration:none; }
.c-lb-i i { font-style:normal; font-family:var(--font-mono); font-size:10px;
            font-weight:400; color:var(--ink-3); margin-top:1px; }
.c-lb-i:hover { border-color:var(--gold-lt); color:var(--gold-dk); }
.c-lb-i.c-on { background:var(--gold-solid); border-color:var(--gold-solid);
          color:var(--gold-solid-text); }
.c-lb-i.c-on i { color:var(--gold-solid-text); opacity:.75; }

/* ── rashi strip ──────────────────────────────────────── */
.c-rs { display:grid; gap:8px; grid-template-columns:repeat(3,1fr); }
@media (min-width:560px) { .c-rs { grid-template-columns:repeat(4,1fr); } }
@media (min-width:900px) { .c-rs { grid-template-columns:repeat(6,1fr); } }
.c-rs > * { min-width:0; }
.c-rs-i { display:flex; flex-direction:column; align-items:center; gap:3px;
          min-height:44px; padding:12px 6px; border-radius:var(--r-card);
          border:1px solid var(--line); background:var(--surface);
          text-decoration:none; color:var(--ink); }
.c-rs-i:hover { border-color:var(--gold); background:var(--gold-bg); }
.c-rs-i.c-on { border-color:var(--gold-solid); background:var(--gold-bg); }
.c-rs-i b { font-size:13px; font-weight:650; }
.c-rs-i i { font-style:normal; font-size:11px; color:var(--ink-3); }
.c-rs-i .c-sym { margin-bottom:2px; }

/* .c-rp-top takes the free space now, so the tear and the stub sit at the
   foot of every card in a row whatever the description did above them —
   which is what margin-top:auto on the pay block used to do, moved one
   element up so the divider travels with the stub rather than floating. */
.c-rp .c-rp-top, .c-rp.c-rp-sm .c-rp-top { flex:1; }

/* ── long-form prose ──────────────────────────────────── */
.c-prose { max-width:66ch; margin-inline:auto; }
.c-prose h2 { font-family:var(--font-serif); font-size:clamp(19px,2.6vw,24px);
              line-height:1.22; letter-spacing:-.016em; font-weight:600;
              margin:34px 0 12px; text-wrap:balance; }
.c-prose h2:first-child { margin-top:4px; }
.c-prose p { font-size:var(--text-base); line-height:1.72; color:var(--ink-2);
             margin:0 0 14px; }
.c-prose ul { margin:0 0 14px; padding-left:20px; }
.c-prose li { font-size:var(--text-base); line-height:1.72; color:var(--ink-2);
              margin:0 0 7px; }
.c-prose a { color:var(--gold-dk); }
/* When it was last looked at, said once, before the text rather than buried
   at the foot of it — on a legal page that is the first thing worth knowing. */
.c-prose-n { font-family:var(--font-mono); font-size:11px; letter-spacing:.06em;
             text-transform:uppercase; color:var(--ink-3);
             margin:0 0 22px !important; }

/* ── A TYPE FLOOR ON PHONES ───────────────────────────────
   Measured at 390x844 on the landing page: 117 text nodes under 12px, 67 of
   them at 9.5px or below and 17 at 8px. Most of that is deliberate — the mini
   panchang, the sample kundli and the chat simulation are PICTURES of a user
   interface, and their type is small the way type in a screenshot is small.
   Enlarging those would break the illustration, so they are left alone.

   These are the ones that are NOT pictures. They are labels on the real page,
   set at 10px because a mono eyebrow at 10px looks crisp on a monitor at
   arm's length — and it is the same 10px in a hand at a third of the
   distance. 11px, with the tracking they already carry, stays an eyebrow and
   becomes readable. */
@media (max-width:640px) {
  .c-label, .c-hero-e, .c-foot-h { font-size:11px; }
}

/* ── the folding hero lede ────────────────────────────────
   Only on a phone. On anything wider the paragraph is the right length and
   the control would be furniture with no purpose. */
/* ── "Answering now", with a live dot ──
   The hero eyebrow was three claims joined by middots — no subscription, no
   auto-renewal, astrologers in हिंदी — which at 390px wrapped to two lines of
   10px mono before the reader reached the headline. This is one fact, and it
   is the one that matters at the top of a page selling a conversation:
   somebody is there.

   Reuses @keyframes c-orb-pulse, already defined for the orbit's own status
   dot, rather than declaring a second pulse — a duplicate @keyframes with the
   same name replaces the first one globally, which this file has been bitten
   by before and says so at the definition.

   The dot is decorative and the words carry the meaning, so it is
   aria-hidden and there is nothing for a screen reader to trip over. */
.c-hero-live { display:inline-block; width:7px; height:7px; border-radius:50%;
               background:var(--good); margin-right:7px; vertical-align:middle;
               animation:c-orb-pulse 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion:reduce) { .c-hero-live { animation:none; } }
.c-hero-lb { display:none; }
@media (max-width:640px) {
  .c-hero-lc { display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical;
               overflow:hidden; margin-bottom:6px; }
  .c-hero-lb { display:inline-flex; align-items:center; min-height:44px;
               cursor:pointer; font-size:14px; font-weight:650;
               color:var(--gold-dk); }
  .c-hero-lb::after { content:'Read more'; }
  /* :has() is not needed — the checkbox is a previous sibling of both. */
  #c-hero-lm:checked ~ .c-hero-lc { -webkit-line-clamp:unset; }
  #c-hero-lm:checked ~ .c-hero-lb::after { content:'Show less'; }
  /* The keyboard has to see it: .c-radio is visually hidden, so the ring
     goes on the label the same way the drawers do it. */
  #c-hero-lm:focus-visible ~ .c-hero-lb { outline:2px solid var(--gold);
                                          outline-offset:3px; border-radius:6px; }
}

/* ── 11px IS THE FLOOR ON A PHONE, ON THE PUBLIC PAGES TOO ──
   The signed-in shell got this in appshell.py; /kundli/ is the same argument
   on the other side of the sign-in. Six rules put about fifty strings between
   6.5px and 10px there: both birth-form field labels, the example line under
   them, every readout label on the sample chart ("Lagna · ascendant", "Yoni",
   "Nama akshar"), the chart's own metadata row, and the house numbers inside
   the diagram.
   `body` prefixes each selector for the same reason .d-main did in the shell:
   page CSS is emitted after this file, so specificity rather than position is
   what makes these win. */
@media (max-width:640px) {
  body .c-pf-l, body .c-field label, body .c-eg-line,
  body .h-f-l, body .j-lb-f i,
  /* The five panels of the kundli view carry their own set: every table
     heading, the sub-label under each of the twenty-three chart tabs, the
     dasha ribbon's graha names and the phase dates beside them. They only
     appear on /home/kundli/ behind #j-t-grahas, #j-t-vargas, #j-t-yogas and
     #j-t-dasha, which is why the bare path measured clean for weeks. */
  body .j-tbl thead th, body .j-cp-t thead th, body .j-ct > i,
  body .j-vz-ph > i, body .j-rb-s, body .j-rb-s > b { font-size:11px; }
  /* ── THREE SHARED COMPONENTS, FOUND ON /home/support/ ──
     .c-badge is the status word on a support ticket — OPEN, IN PROGRESS,
     RESOLVED — and wherever else a badge is used it is doing the same job:
     saying what state a thing is in. .c-count is the "0 / 5000" under a
     message box, which is the only warning a reader gets before the field
     stops accepting characters. And .c-fs legend is the question over a
     fieldset — "What can we help with?" — which names the whole group.
     They were 9.5, 9.5 and 10. Fixed here rather than on the page, because
     the size is wrong wherever they land, not only there. */
  body .c-badge, body .c-count, body .c-fs legend { font-size:11px; }
  /* ── THE TWO FLOOR LISTS HAD DRIFTED APART ─────────────────────────────
     appshell.py carries the same block for the signed-in side, and it floors
     eleven shared components from this very file that this list did not.
     Only .c-pf-l was in both. Every one of them is built here and rendered on
     both sides, so the size was right on a signed-in phone and wrong on a
     public one — the same component, the same reader, a different answer
     depending on which half of the site they happened to be on.

     Found on /compatibility/, where three of them land at once: the link
     bar's sub-label is carrying the SCORE of each pair it links to, at 10px,
     which is the number somebody picks a pair by; and the scale bar's twelve
     values and twelve rashi labels — the whole content of the sign page — sat
     at 10.5 and 9.5.

     .c-tl-* is the day timeline, and it is why /panchang/ has been drawing
     its row labels at 8.5px while /home/panchang/ drew the same labels at 11.
     The label column that had to be widened to hold them is in this file and
     was widened for both; only the size stayed behind.

     LISTED, NOT COPIED WHOLESALE. appshell's block also floors .d-* rules,
     for pages that exist only behind sign-in.

     THIS PARAGRAPH USED TO SAY .h-* AND .j-* WERE IN THAT CATEGORY TOO, and
     it was wrong — asserted rather than checked, and both were public all
     along. .j-* is /kundli/ and was already in the list below; .h-* is the
     ten horoscope pages, and only .h-f-l of it had made the crossing, which
     is what the next block is. Counted over the built site this time: every
     one of the ten .h-* classes renders on public pages, and .d-fx, .dl-ls,
     .dl-seg and .st-pill-off render on none. */
  body .c-lb-i i,
  body .c-scl-l, body .c-scl-t, body .c-scl-tag, body .c-scl-v,
  body .c-table th,
  body .c-tl-c b, body .c-tl-lbl, body .c-tl-night i, body .c-tl-now i,
  body .c-tl-t i { font-size:11px; }
  /* ── AND TWO NEITHER LIST HAD ──
     .c-stat is the label over a number — "distinct scores", "pairs move" —
     at 9.5px site-wide, which makes the caption smaller than anything else
     in the card while the number above it is 28px. It renders on no
     signed-in page, which is the only reason the other list never caught it.
     .c-mx th is the sign name over each column of a compatibility grid. The
     grid already scrolls sideways and says so, so the extra width costs
     scrolling rather than legibility — which is the trade that component was
     built to make. */
  body .c-stat span, body .c-mx th { font-size:11px; }
  /* ── THE HOROSCOPE HALF OF THE SAME DRIFT ──
     Ten public pages built from pages/horoscope.py, and every one of these is
     already floored for the signed-in reader by the identical block in
     appshell.py. What was under the floor was most of what those pages are:
     the Sanskrit name on each of the twelve rashi tiles (Mesha, Kumbha), the
     score on each bar of the twelve-sign chart, the three-letter code under
     it, the eyebrow over every section, the five labels of the year's radar,
     the trend word — ascending, mixed, descending — and the luck strip's
     captions.
     .h-f-bs and .h-f-hint-m are not listed because they set no size of their
     own; they inherit from .h-f-bn and .h-f-hint, which are.
     The tiles were ALREADY built for this. horoscope.py carries a phone block
     reading "Ari needs 20px at the 11px phone floor and the tile gave it 18",
     and another that scrolls the bar chart sideways below 360 for the same
     reason. The accommodation was written; the floor that needed it never
     reached these pages. */
  body .h-dl-r .h-dl-pc, body .h-f-b u, body .h-f-bn, body .h-f-hint,
  body .h-f-luck-t, body .h-f-tile i, body .h-trend, body .h-f-eyebrow,
  body .h-radar-l { font-size:11px; }
  /* /horoscope/chinese/ is its own builder and its own two rules: the heads
     of the sixty-year table, and the element-and-polarity line under each of
     the twelve animals — "Earth · Yang", which is the whole of what the tile
     says beyond the name. */
  body .z-tbl th, body .z-tile i { font-size:11px; }
  /* ── THE PANCHANG HALF, WHICH IS THE FOURTH TIME THIS LIST HAS BEEN SHORT ──
     appshell.py floors these four for the signed-in reader and this one had
     none of them: the heading over each of the five angas, the one over each
     block of hours, the one over each choghadiya table, and the line under
     the day's summary. /panchang/ and its six dated days, all public. */
  body .p-anga-h, body .p-ch-h, body .p-per-h, body .p-sum i,
  /* And the heads of the anga tables — the fifteen tithis, the twenty-seven
     nakshatras, the twenty-seven yogas, the eleven karanas, the seven vaars
     and the hora — where "Day" and "Runs" name the two columns the whole
     table is. Page-owned, so neither list had it. */
  body .pg-t thead th { font-size:11px; }
  /* ── TWO THAT REACH FURTHER THAN THIS SECTION ──
     .s-eyebrow is lib/shell.py's, sitting over a section head on any public
     page; .x-tbl th is the calculator engine's table head, shared by every
     tool pages/views builds. Both surfaced on /panchang/rahu-kaal/ and
     /panchang/choghadiya/ — calculators that happen to live in this section —
     at 10px and 9.5px, and both are wrong wherever they land.

     .x-big i AND .x-big em ARE NOT HERE, AND A FLOOR IS WHY. Adding them read
     as harmless and was not: `body .x-big i` outranks lastsix.py's plain
     `.x-big i`, which sets 16px, so a rule written to raise type on two pages
     would have SHRUNK the caption under the big answer on every other
     calculator on the site. The two that are genuinely small are
     clamp()-sized against their own container inside a ring, and their
     minimums are fixed where they are written. A floor block can only ever
     be a floor if nothing it overrides was already above it. */
  body .s-eyebrow, body .x-tbl th { font-size:11px; }
  /* ── THE SAME TABLE HEAD, WRITTEN TWELVE TIMES ─────────────────────────
     This site has no shared table component. It has twelve of them, each a
     near-copy of the next under its own prefix — .adm-tbl, .b-tbl, .c-table,
     .d-tbl, .f-tbl, .fm-t, .j-tbl, .n-tbl, .pc-tbl, .pg-t, .q-tbl, .t-tbl,
     .w-tbl, .x-tbl, .z-tbl — and all but one set their head under the floor.
     Thirteen was the count before /calculators/ turned up a fourteenth.

     THAT IS WHY THIS FLOOR HAS TAKEN FIVE PASSES TO WRITE. .c-table th went
     in with the compatibility grid, .z-tbl th and .pg-t with the horoscope
     and panchang sections, .x-tbl th with the calculators that live under
     /panchang/, .adm-tbl th with the admin panel — each found separately,
     each fixed separately, every one of them the same defect wearing a
     different prefix. The six below are the rest, and they are what made
     eleven of the forty-two calculator pages fail on that one string alone.

     ENUMERATED, NOT `body th`. A bare selector would SET every table head on
     the site to 11px, including the ones already larger — the same trap
     `body .x-big i` was caught in above, where a rule written to raise type
     would have shrunk it. A floor has to be a floor.

     The real fix is one table component and it is not this; a rule that has
     to list twelve prefixes is the duplication reporting itself. */
  /* ── THREE SHARED COMPONENTS THE CALCULATORS SURFACED ──
     .c-nak is the nakshatra strip — twenty-seven cells, each a name and its
     lord, at 8.5px; it is the whole answer on /calculators/nakshatra/ and it
     also draws on baby-name and birthstone. .c-tt is the tithi strip, same
     shape, 9px, on moon-phase. .c-gs-i is the gemstone picker's caption at
     10px. All three are built here and none had ever been floored. */
  body .c-nak b, body .c-nak em, body .c-tt b, body .c-tt em,
  body .c-gs-i i, body .c-chip sup,
  body .b-tbl th, body .d-tbl th, body .f-tbl th, body .fm-t th,
  body .n-tbl th, body .pc-tbl th, body .q-tbl th, body .t-tbl th,
  body .w-tbl th { font-size:11px; }
  /* ── NO CLAMP AT TWO ACROSS ──
     This used to raise the clamp from two lines to three, and said why: at
     320 the third line was where the sentence ended, and "…Saves.." cut the
     number the bundle is sold on. Its reason was "a phone shows one card
     per row, so the extra line costs a line rather than a ragged row".
     THAT PREMISE IS GONE. .c-cat-grid is two across from 360 now, the card
     is 138–183px rather than 358, and the same sentence needs five lines —
     so three would cut the saving again, which is the thing that rule
     existed to protect.
     The clamp comes off instead. Ragged is not a risk here: these are grid
     items, they stretch to their row's height, and .c-rp-s carries flex:1
     so the button still sits on the bottom edge of every card in the row.
     It also removes a measurement artifact — a clamped box reported a
     scrollWidth 3px past its own content box while every line inside it fit,
     which the phone sweep reads as clipped text. The one-card-per-row case
     is below, where its reasoning still holds. */
  /* ── AND ON A PHONE THE SENTENCE GOES ALTOGETHER ──
     The clamp reasoning above is kept because it is the record of why a
     clamp is the wrong tool here, but the sentence itself no longer earns
     its space at this width. Two across on a phone the card is 138-183px
     wide, so `short` wraps to five or six lines and pushes the price and
     the button below the fold on every card in the first row. The title,
     the Devanagari under it and the cover already say which report this
     is; the sentence is what the report's own page opens with.
     THE SAVING GOES WITH IT, and that is the cost of this. A bundle's
     sentence carries "Saves &#8377;246" — the one figure REPORT_OFFER_JS
     cannot restate in another currency, and the reason the clamp was
     fought over twice above. It is still on the bundle's own page and on
     the wide layout; it is gone from a phone shelf. Lift it out of
     .c-rp-d into its own element if it needs to come back. */
  body .c-rp.c-rp-sm .c-rp-d { display:none; }
  /* ── THE CHART KEEPS ITS OWN SIZES, AND THIS IS A CORRECTION ──
     These three were raised here — grahas to 16, rashi numbers to 13 — so
     that the viewBox scaling would land them on the 11px floor. On the worked
     example that shipped clean. On a REAL chart it does not: three grahas
     share a bhava in plenty of charts, and at 16 the labels collide. Su over
     Ju over Ve is unreadable in a way 10px numerals are not.
     The measurement that said otherwise counted a <text> and the <tspan>
     inside it as an overlapping pair — every chart reported the same constant
     number, which should have been the tell — so real crowding was masked by
     an artefact of the detector.
     Measured properly, the ceiling before collision is 11.5/10 at 390 and the
     ORIGINAL 11/9.5 at 320. Reaching the floor inside this diagram needs the
     graha layout to give a crowded bhava more room, which is a larger change
     than a font size and not one to make blind. Until then the chart is left
     as it was, and the check asserts what actually matters in a diagram —
     that nothing overlaps. Every graha's exact position is in the readout
     table under it, in body text, at a size the floor does cover. */
}

/* ── AND BELOW 360, WHERE ONE CARD STILL FILLS THE ROW ──
   Everything the three-line rule said is still true here: the card is the
   full width, the sentence ends on the third line, and clamping to two cut
   "Saves ₹246" — the number the bundle is sold on. */
/* The three-line rule that used to live here clamped a paragraph that is
   now hidden at every phone width, so it had nothing left to clamp. Its
   reasoning is preserved in the note above. */


.c-ico, .c-sym { width:20px; height:20px; flex:none; display:inline-block;
         vertical-align:-.22em; fill:none; stroke:currentColor;
         stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
/* A rashi carries the brand where a UI icon stays in the text colour, and it
   is usually shown larger, so it is drawn a touch lighter to compensate. */
.c-sym { width:24px; height:24px; stroke-width:1.55; color:var(--gold-dk); }
.c-sym-lg { width:34px; height:34px; stroke-width:1.4; }
/* A rashi is a glyph, so it is sized by font-size and not by a box. The stack
   names the faces that actually carry U+2648–2653 before falling back.

   display:inline, NOT inline-block: as an inline-block the glyph brought its
   own line box and had to be nudged back onto the text baseline by hand, which
   put it 7px low the moment it appeared inside 10.5px uppercase mono. Inline,
   it sits on the shared baseline like any other character and needs no
   vertical-align at all. In the tile and the strip it is a flex item, which
   blockifies it, so the centring there is unaffected. */
.c-sym-g { display:inline; width:auto; height:auto; font-size:22px;
           line-height:1; vertical-align:baseline;
           font-family:"Apple Symbols","Segoe UI Symbol","Noto Sans Symbols 2",
                       "Noto Sans Symbols","DejaVu Sans",serif;
           font-variant-emoji:text; -webkit-font-feature-settings:normal; }
.c-sym-g.c-sym-lg { font-size:32px; }
.c-bnd { width:20px; height:20px; flex:none; display:inline-block;
         vertical-align:-.22em; fill:currentColor; stroke:none; }


.c-cv { width:100%; height:100%; display:block;
        --cv-a:#FFF6E6; --cv-b:#F3E3C4; --cv-ink:#8A5A0B; --cv-line:#D9BE86; }
:root[data-theme="dark"] .c-cv, #th-dark:checked ~ * .c-cv {
        --cv-a:#241C13; --cv-b:#17120C; --cv-ink:#D9A94F; --cv-line:#4A3A22; }


.l-app { background:var(--cream); color:var(--ink); min-height:100vh; }
#th-dark:checked ~ .l-app { --cream:#17120C;--surface:#1F1810;--surface-2:#241C13;--rail:#1B150E;--ink:#F6EFE3;--ink-2:#CFC2AC;--ink-3:#9A8B75;--line:#332A1E;--line-soft:#2A2217;--gold:#D9A94F;--gold-dk:#E8C98A;--gold-lt:#8A6A2E;--gold-bg:#2A2114;--good:#4EA87A;--mid:#D98E43;--bad:#D4635A;--good-bg:#1B2620;--mid-bg:#2A2115;--bad-bg:#2A1A18;--pill-ink:#F6EFE3;--pill-text:#17120C;--gold-solid:#D9A94F;--gold-solid-text:#17120C;--sk:#3A2F1E;--sk-hi:#54452C;--good-solid:#4EA87A;--good-solid-text:#17120C;--ph:#93846D;--cat-1:#E5B865;--cat-2:#AE5638;--cat-3:#D5DCEC;--cat-4:#4FA69E; }
#th-light:checked ~ .l-app { --cream:#FFFAF3;--surface:#FFFFFF;--surface-2:#FAF4E9;--rail:#F6EFE2;--ink:#17120C;--ink-2:#4A3F31;--ink-3:#756853;--line:#E8DCC8;--line-soft:#F0E7D6;--gold:#C6943E;--gold-dk:#92600A;--gold-lt:#E8C98A;--gold-bg:#FBF3E3;--good:#2F7A57;--mid:#9E5A18;--bad:#B3261E;--good-bg:#EAF3ED;--mid-bg:#FBF0E4;--bad-bg:#FAEBEA;--pill-ink:#231C13;--pill-text:#FFFAF3;--gold-solid:#8A5A0B;--gold-solid-text:#FFF9F0;--sk:#E4D8C2;--sk-hi:#F7F1E6;--good-solid:#2F7A57;--good-solid-text:#FFFFFF;--ph:#7C6E5A;--cat-1:#6B4708;--cat-2:#D47142;--cat-3:#262C3A;--cat-4:#24756F; }
/* Which icon is offered: the one you are not in. With no choice made that
   is always dark now, because light is where everybody starts. */
.c-th-to-light { display:none; }
/* A REMEMBERED THEME HAS TO MOVE THE ICON AT FIRST PAINT, not when the radio
   is ticked a moment later. Somebody who chose dark on a light laptop would
   otherwise load every page dark with the moon showing — an offer to switch
   to the mode they are already in. These are keyed on the attribute the head
   script sets, so they land in the same paint as the palette. */
:root[data-theme="dark"] .c-th-to-dark { display:none; }
:root[data-theme="dark"] .c-th-to-light { display:grid; }
:root[data-theme="light"] .c-th-to-dark { display:grid; }
:root[data-theme="light"] .c-th-to-light { display:none; }
#th-dark:checked ~ .l-app .c-th-to-dark { display:none; }
#th-dark:checked ~ .l-app .c-th-to-light { display:grid; }
#th-light:checked ~ .l-app .c-th-to-dark { display:grid; }
#th-light:checked ~ .l-app .c-th-to-light { display:none; }
