* { box-sizing:border-box; }
  body { margin:0; background:var(--cream); color:var(--ink);
         font-family:var(--font-sans); font-size:15px; line-height:1.6; }
  /* Grid and flex children default to min-width:auto, which lets a wide table
     push the whole page sideways. This is the rule the signed-in mockup was
     missing on all seventeen routes. */
  .l-wrap > *, .l-sec > * { min-width:0; }
  .l-wrap { max-width:var(--wrap); margin:0 auto; padding:0 16px; }
  .l-sec { padding:38px 0; }
  .l-alt { background:var(--surface-2); border-block:1px solid var(--line-soft); }
  .l-sub { font-size:var(--text-base); line-height:1.62; color:var(--ink-2);
           margin:0; max-width:66ch; }
  /* A link in a lede had no colour and came out in the browser's own
     -webkit-link — rgb(158,158,255) in dark, #0000EE in light. .l-sub is on
     every page, so this was one page today and any page that adds a link to a
     lede tomorrow. Set here rather than on the page that happened to have
     one. */
  .l-sub a { color:var(--gold-dk); text-underline-offset:2px; }
  /* A CENTRED SECTION, for pages whose head is centred too. Lives here rather
     than in a page's stylesheet because two clusters use it now — it was
     .ai-c in ai_astrologer.py, and pages/dasha.py picked up the class without
     the rule, so its ledes rendered left under centred heads and looked like
     an oversight. margin-inline is not optional: .l-sub caps at 66ch, so
     centring the text without centring the box leaves a centred column
     sitting on the left of the wrap.
     Only the lede. Lists, tables and cards inside a .l-ctr stay left —
     centred body copy is ragged on both edges and a centred bulleted list
     stops forming a column to scan. */
  .l-ctr .l-sub { text-align:center; margin-inline:auto; }
  /* TWO LEDES GO SIDE BY SIDE. One is fine stacked: 66ch keeps a line
     readable and the grid or panel under it carries the width. Two of them
     stacked is not — they fill the left half of the band, the right half is
     empty, and under a full-width heading with a full-width panel below it
     the pair reads as a column that has been cut off rather than as air.
     Measured at 1280: 582px of a 1180px section, 49% of it, twice over.

     A track each, so both keep the SAME measure they had — this is a layout
     fix, not a wider line. Stretching one paragraph across 1180px would give
     150 characters to a line that is already at 74.

     auto-fit rather than two fixed columns, because the home page has a pair
     inside a half-width column; there they stack again rather than becoming
     two forty-character ribbons.

     .l-sub KEEPS ITS 66ch. Each track comes out at 552px against a 582px
     cap, so the track is what governs in two columns and the cap is what
     governs when it falls back to one — which is the width it has today.
     Overriding the cap to none looked tidier and gave a 736px tablet a
     93-character line, which is the fault this rule exists to avoid.

     min(400px, 100%), NOT 400px. A minimum track wider than the container
     does not shrink — it overflows it, and at 320px the whole page scrolled
     sideways by 26px. min() caps the floor at whatever room there is. */
  .l-lede { display:grid; gap:10px 44px; margin-bottom:20px;
            grid-template-columns:repeat(auto-fit, minmax(min(400px, 100%), 1fr)); }
  .l-mb { margin-bottom:20px; }
  .l-mt { margin-top:22px; }
  .l-signin, .l-signup { text-decoration:none; white-space:nowrap; }
  .l-act-u, .l-act-a { display:flex; align-items:center; gap:8px; }
  /* The drawer carries Sign in and Sign up and nothing else now — the theme
     toggle moved to top_nav's `bar`, which is the header at every width. Its
     own display and gap belong to the component, in ui.py, so the breakpoint
     that hides it there is not overruled from here. */
  .c-drawer-a .l-signin, .c-drawer-a .l-signup {
      flex:1; min-height:44px; padding:0 15px; justify-content:center; }
  /* Date and time are one question, asked once. Both values are short enough
     to sit side by side even at 360px, and pairing them stops the form
     reading as five separate chores. */
  /* Date and time side by side, and STACKED on the narrowest phones. The two
     were always a 1fr 1fr grid; what changed is that the time trigger now
     reads 09:15 PM as four flex spans rather than one string, so its
     min-content is wider than half of a 288px card and the card grew to 311 —
     7px of sideways scroll on the page the whole site links to. minmax(0,1fr)
     alone would have kept the row and clipped the readout instead. */
  .l-dt { display:grid; grid-template-columns:1fr; gap:10px; }
  @media (min-width:360px) {
    .l-dt { grid-template-columns:minmax(0,1fr) minmax(0,1fr); }
  }
  /* Part of the hero's left column, not a band of its own. It anchors the
     bottom of that column so it finishes level with the form card. */
  /* Inside the form card the list sits under the button, separated by a rule
     rather than by more whitespace — it is what pressing the button gets you,
     so it should read as part of the button's promise. */
  .l-cardsub { margin:2px 0 14px; }
  .l-cardinc { margin-top:14px; padding-top:14px;
               border-top:1px solid var(--line-soft); }
  .l-inc { list-style:none; margin:0; padding:0; display:grid; gap:9px; }
  .l-inc li { display:flex; align-items:flex-start; gap:9px; }
  .l-inc svg { flex:none; margin-top:2px; color:var(--gold-dk); }
  .l-inc span { font-size:14px; line-height:1.45; color:var(--ink-2); }

  .l-samp { display:flex; align-items:center; gap:13px; padding:14px 16px;
            text-decoration:none; color:var(--ink); border:1px solid var(--line);
            border-radius:var(--r-card); background:var(--surface); }
  .l-samp:hover { border-color:var(--gold); background:var(--gold-bg); }
  /* Between the two-column breakpoint and 1024 the left column runs 9px past
     the card, because the headline takes an extra line before the columns are
     wide enough. Four pixels off the padding and four off the stack gap close
     it without anyone being able to tell the difference. */

  .l-samp > svg:first-child { flex:none; color:var(--gold-dk); }
  .l-samp > svg:last-child { flex:none; color:var(--ink-3); margin-left:auto; }
  .l-samp span { min-width:0; }
  .l-samp b { display:block; font-size:14.5px; font-weight:700; }
  .l-samp i { display:block; font-style:normal; font-size:12.5px;
              color:var(--ink-3); margin-top:2px; line-height:1.45; }
  .l-dt .c-pf { margin-bottom:0; }
  .l-dth { margin:6px 0 var(--space-3); }
  .l-mb2 { margin-bottom:16px; }

  .l-mark { display:block; width:34px; height:34px; flex:none; }
  .l-mark img { display:block; width:100%; height:100%; border-radius:19%; }

  /* proof row */
  .l-proof { background:var(--gold-bg); border-block:1px solid var(--line); }
  .l-pfr { display:grid; grid-template-columns:repeat(2,1fr); gap:0;
           padding:4px 0; }
  .l-pf { padding:18px 4px 18px 18px; }
  /* Hairlines, not a colour change, do the separating — and only between
     cells that are actually side by side, which changes at each breakpoint. */
  .l-pf:nth-child(2n) { border-left:1px solid var(--line); }
  .l-pf:nth-child(n+3) { border-top:1px solid var(--line); }
  .l-pf:first-child { padding-left:0; }
  .l-pf b { display:block; font-family:var(--font-serif); font-size:29px;
            line-height:1.05; font-weight:600; color:var(--gold-dk);
            letter-spacing:-.015em; }
  .l-pf span { display:block; font-size:13.5px; font-weight:650; margin-top:5px;
               color:var(--ink); }
  .l-pf i { display:block; font-style:normal; font-size:12.5px; color:var(--ink-3);
            margin-top:2px; }
  @media (min-width:760px) {
    .l-pfr { grid-template-columns:repeat(4,1fr); }
    .l-pf:nth-child(n+3) { border-top:0; }
    .l-pf:nth-child(n+2) { border-left:1px solid var(--line); }
  }

  /* panchang */
  .l-panch { display:grid; gap:8px; grid-template-columns:repeat(2,1fr); }
  .l-pc { padding:13px 14px; border:1px solid var(--line); border-radius:var(--r-in);
          background:var(--surface); }
  .l-pc span { display:block; font-family:var(--font-mono); font-size:9px;
               letter-spacing:.13em; color:var(--ink-3); }
  .l-pc b { display:block; font-size:15px; margin-top:5px; }
  .l-pc i { display:block; font-style:normal; font-size:12px; color:var(--ink-3); }
  .l-pc.c-t-good b { color:var(--good); } .l-pc.c-t-bad b { color:var(--bad); }
  @media (min-width:700px) { .l-panch { grid-template-columns:repeat(3,1fr); } }
  @media (min-width:1000px) { .l-panch { grid-template-columns:repeat(6,1fr); } }

  /* rashifal */
  .l-rsg { display:grid; gap:7px; grid-template-columns:repeat(3,1fr); }
  .l-rs { display:flex; flex-direction:column; align-items:center; gap:1px;
          min-height:98px; justify-content:center; padding:12px 5px;
          border:1px solid var(--line); border-radius:var(--r-in);
          background:var(--surface); text-decoration:none; color:var(--ink);
          text-align:center; }
  .l-rs:hover { border-color:var(--gold); background:var(--gold-bg); }
  .l-rs .c-sym { color:var(--gold-dk); margin-bottom:4px; }
  .l-rs b { font-size:12.5px; font-weight:650; }
  .l-rs i { font-style:normal; font-size:10.5px; color:var(--ink-3); }
  .l-rs u { text-decoration:none; font-size:12px; color:var(--gold-dk); }
  @media (min-width:560px) { .l-rsg { grid-template-columns:repeat(4,1fr); } }
  @media (min-width:900px) { .l-rsg { grid-template-columns:repeat(6,1fr); } }

  /* tools */
  .l-tools { display:grid; gap:12px; }
  .l-ti { display:grid; place-items:center; width:40px; height:40px;
          border-radius:11px; color:var(--gold-dk); background:var(--gold-bg);
          margin-bottom:11px; }
  .l-tl { display:flex; flex-wrap:wrap; gap:6px; margin-top:13px; }
  .l-tl a { display:inline-flex; align-items:center; min-height:44px;
            font-size:12.5px; font-weight:600; padding:0 14px;
            border-radius:var(--r-pill); color:var(--ink-2);
            border:1px solid var(--line); background:var(--surface);
            text-decoration:none; }
  .l-tl a:hover { color:var(--gold-dk); border-color:var(--gold);
                  background:var(--gold-bg); }
  @media (min-width:720px) { .l-tools { grid-template-columns:repeat(2,1fr); } }

  /* astrologers */
  /* .h-astro in home.py sets display on top of this and is the only caller,
     so anything layout-ish added here is dead. The phone rail lives there. */
  .l-astro { display:grid; gap:11px; grid-template-columns:repeat(1,1fr); }
  .l-ah { display:flex; align-items:center; gap:11px; }
  .l-an { display:block; font-size:15px; font-weight:700; }
  .l-as { display:block; font-size:12.5px; color:var(--ink-3); }
  .l-am { display:flex; gap:5px; margin-top:12px; }
  @media (min-width:600px) { .l-astro { grid-template-columns:repeat(2,1fr); } }
  @media (min-width:1000px) { .l-astro { grid-template-columns:repeat(4,1fr); } }

  /* The unknown-birth-time escape hatch belongs inside the time picker, where
     someone reaches it at the exact moment they realise they cannot answer. */
  .l-unk { margin-top:14px; padding-top:12px; border-top:1px solid var(--line-soft); }
  .l-faq { margin-top:16px; }


/* ── page head ────────────────────────────────────────── */
.s-crumb { padding-top:16px; }
.s-top { padding:22px 0 6px; }
.s-head { max-width:64ch; }
/* With a form beside it the header is the full width and the two columns
   share it, so the reading underneath is not squeezed into a rail. */
.s-head-2 { max-width:none; display:grid; gap:26px;
            grid-template-columns:minmax(0,1fr); align-items:start; }
.s-head-2 > * { min-width:0; }
@media (min-width:960px) {
  .s-head-2 { grid-template-columns:minmax(0,1fr) minmax(0,380px); }
}
.s-head-t { max-width:64ch; }
.s-eyebrow { 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; }
.s-h1 { font-family:var(--font-serif); font-size:clamp(27px,5.2vw,42px);
        line-height:1.08; letter-spacing:-.021em; font-weight:600;
        margin:0 0 13px; text-wrap:balance; }
.s-h1 em { font-style:normal; color:var(--gold-dk); }
.s-dek { font-size:var(--text-md); line-height:1.62; color:var(--ink-2);
         margin:0; max-width:58ch; }
/* What the page gives back, named. See page_head() for why it is only on the
   fourteen with a full birth-details form. */
.s-ret { margin-top:18px; max-width:58ch; }
.s-ret ul { margin:8px 0 0; padding:0; list-style:none; display:grid;
            gap:5px; }
.s-ret li { position:relative; padding-left:18px; font-size:14px;
            line-height:1.55; color:var(--ink-2); }
.s-ret li::before { content:''; position:absolute; left:2px; top:9px;
                    width:5px; height:5px; border-radius:50%;
                    background:var(--gold-solid); }
@media (min-width:960px) {
  .s-head-fill { align-items:stretch; }
  .s-head-fill > .s-head-t { display:flex; flex-direction:column; }
  .s-head-fill .s-meta { margin-top:auto; padding-top:16px; }
}
/* The facts a dek would otherwise carry as adjectives. Mono, so they read as
   data rather than as more sentence. */
/* THE CENTRED HEAD, for pages that are not asking for a birth time. The
   default sits beside a form card and runs to 64ch down the left; a page
   whose top is a choice rather than a form leaves that whole right-hand
   column empty, and the text reads as the caption of something that failed
   to load. Two pages want this — the tarot hub, whose top half is six
   readings, and the astrologer pages, whose top half is a roster — so it
   lives here rather than being written twice. */
.s-head-c { max-width:700px; margin-inline:auto; text-align:center; }
.s-head-c .s-eyebrow { font-size:11.5px; letter-spacing:.09em; font-weight:700;
          border:1px solid var(--gold-lt); padding:5px 13px; margin-bottom:16px; }
.s-head-c .s-h1 { font-size:clamp(30px,5.6vw,46px); }
.s-head-c .s-dek { margin-inline:auto; max-width:62ch; }
.s-head-c .s-meta { justify-content:center; }

.s-meta { display:flex; flex-wrap:wrap; gap:8px 18px; margin-top:16px;
          font-family:var(--font-mono); font-size:11px; letter-spacing:.04em;
          color:var(--ink-3); }
.s-meta span { display:flex; align-items:center; gap:7px; }
.s-meta span + span::before { content:''; width:3px; height:3px; flex:none;
          border-radius:50%; background:var(--line); margin-left:-11px; }

/* ── related ──────────────────────────────────────────── */
.s-rel { display:grid; gap:10px; grid-template-columns:1fr; }
@media (min-width:620px) { .s-rel { grid-template-columns:repeat(2,1fr); } }
@media (min-width:960px) { .s-rel { grid-template-columns:repeat(3,1fr); } }
.s-rel > * { min-width:0; }
.s-rel-i { display:flex; align-items:flex-start; gap:12px; padding:14px 16px;
           min-height:44px; text-decoration:none; color:var(--ink);
           background:var(--surface); border:1px solid var(--line);
           border-radius:var(--r-card); }
.s-rel-i:hover { border-color:var(--gold); background:var(--gold-bg); }
.s-rel-k { flex:none; display:grid; place-items:center; width:32px; height:32px;
           border-radius:9px; background:var(--gold-bg); color:var(--gold-dk); }
.s-rel-x { min-width:0; display:flex; flex-direction:column; line-height:1.4; }
.s-rel-x b { font-size:14px; font-weight:650; }
.s-rel-x i { font-style:normal; font-size:12.5px; color:var(--ink-3);
             margin-top:2px; }

/* ── closing CTA ──────────────────────────────────────── */
.s-cta { display:grid; gap:20px; align-items:center; padding:28px;
         border-radius:var(--r-card); border:1px solid var(--gold-lt);
         background:
           radial-gradient(120% 140% at 88% 0%, var(--gold-bg) 0%, transparent 64%),
           var(--surface); }
@media (min-width:820px) {
  .s-cta { grid-template-columns:1fr auto; gap:34px; padding:32px 36px; } }
.s-cta > * { min-width:0; }
.s-cta h2 { font-family:var(--font-serif); font-size:clamp(21px,3.4vw,28px);
            line-height:1.16; letter-spacing:-.018em; font-weight:600;
            margin:0 0 9px; text-wrap:balance; }
.s-cta p { font-size:var(--text-base); line-height:1.6; color:var(--ink-2);
           margin:0; max-width:52ch; }
.s-cta-b { display:flex; flex-wrap:wrap; gap:10px; }
.s-cta-b > * { flex:1 1 auto; }
.s-cta-alt { display:inline-flex; align-items:center; justify-content:center;
             gap:8px; text-decoration:none; white-space:nowrap; }
.s-cta-alt svg { color:var(--gold-dk); }

  /* ── the welcome gift ── */
  .d-bon { position:fixed; inset:0; z-index:60; display:flex;
           align-items:center; justify-content:center; padding:20px; }
  .d-bon[hidden] { display:none; }
  .d-bon-x { position:absolute; inset:0; background:rgba(0,0,0,.5); }
  .d-bon-c { position:relative; width:100%; max-width:390px; text-align:center;
             padding:26px 26px 20px; border-radius:18px;
             border:1px solid var(--line); background:var(--surface);
             box-shadow:0 18px 50px rgba(0,0,0,.22);
             max-height:calc(100dvh - 40px); overflow-y:auto; }
  .d-bon-g { display:inline-flex; align-items:center; justify-content:center;
             width:62px; height:62px; border-radius:50%;
             background:var(--gold-bg); color:var(--gold-dk);
             margin-bottom:13px; }
  .d-bon-c h2 { font-family:var(--font-serif); font-size:23px; font-weight:600;
                margin:0; color:var(--ink); letter-spacing:-.01em; }
  .d-bon-s { margin:5px 0 16px; font-size:13.5px; color:var(--ink-2); }
  .d-bon-amt { padding:15px; border-radius:var(--r-card);
               background:var(--gold-bg); margin-bottom:16px; }
  .d-bon-amt span { display:block; font-family:var(--font-mono);
                    font-size:10px; letter-spacing:.14em; color:var(--ink-3);
                    text-transform:uppercase; }
  .d-bon-amt b { display:block; margin:5px 0 4px; font-family:var(--font-serif);
                 font-size:38px; font-weight:600; line-height:1;
                 color:var(--gold-dk); }
  .d-bon-amt i { font-style:normal; font-size:12px; color:var(--ink-2); }
  .d-bon-l { list-style:none; margin:0 0 18px; padding:0; display:grid;
             gap:8px; text-align:left; }
  .d-bon-l li { position:relative; padding-left:20px; font-size:13px;
                color:var(--ink-2); }
  .d-bon-l li::before { content:''; position:absolute; left:4px; top:.5em;
                width:6px; height:6px; border-radius:50%;
                background:var(--gold-dk); }
  .d-bon-go { position:relative; width:100%; }
  .d-bon-go .d-bon-lb { transition:opacity .12s linear; }
  .d-bon-go[aria-busy="true"] .d-bon-lb { opacity:0; }
  .d-bon-go[aria-busy="true"] { pointer-events:none; }
  .d-bon-sp { position:absolute; top:50%; left:50%; width:20px; height:20px;
              margin:-10px 0 0 -10px; opacity:0; pointer-events:none; }
  .d-bon-go[aria-busy="true"] .d-bon-sp { opacity:1; }
  .d-bon-no { display:block; width:100%; margin-top:9px; font:inherit;
              font-size:12.5px; padding:7px; border:0; background:none;
              color:var(--ink-3); cursor:pointer; }
  .d-bon-no:hover { color:var(--ink-2); text-decoration:underline; }
  .d-bon-e { margin:9px 0 0; font-size:12.5px; color:var(--bad); }
  .d-bon-e[hidden] { display:none; }

  /* The row for whoever closed it. Money nobody has taken should not be
     invisible because a modal was dismissed once. */
  .d-bonr { display:flex; align-items:center; gap:13px; flex-wrap:wrap;
            padding:13px 16px; margin:0 0 18px;
            border:1px solid var(--gold); border-radius:var(--r-card);
            background:var(--gold-bg); }
  .d-bonr[hidden] { display:none; }
  .d-bonr-g { flex:none; color:var(--gold-dk); display:flex; }
  .d-bonr-t { flex:1 1 210px; min-width:0; font-size:13.5px; color:var(--ink); }
  .d-bonr-t b { font-weight:650; }
  .d-bonr-x { flex:none; font:inherit; font-size:12.5px; border:0;
              background:none; color:var(--ink-3); cursor:pointer;
              padding:6px 8px; }
  .d-bonr-x:hover { color:var(--ink-2); text-decoration:underline; }

  /* ── shell ── */
  .d-shell { display:grid; grid-template-columns:248px minmax(0,1fr);
             min-height:100vh; background:var(--cream); }
  .d-col2 { min-width:0; }
  .d-rail { background:var(--rail); border-right:1px solid var(--line);
            padding:16px 12px 12px; display:flex; flex-direction:column;
            gap:3px; position:sticky; top:0; height:100vh; overflow-y:auto; }
  .d-brand { display:flex; align-items:center; gap:10px; padding:4px 8px 16px;
             color:inherit; text-decoration:none;
             font-family:var(--font-serif); font-size:16px; font-weight:600; }
  .d-grp { margin-top:14px; padding:0 8px 6px; }
  .d-grp b { display:block; font-family:var(--font-mono); font-size:9.5px;
             letter-spacing:.14em; text-transform:uppercase; font-weight:600;
             color:var(--ink-3); }
  .d-grp i { display:block; font-family:var(--font-serif); font-style:italic;
             font-size:11.5px; color:var(--ink-3); }
  .d-nav { display:flex; align-items:center; gap:10px; padding:9px 10px;
           border-radius:var(--r-in); font-size:13.5px; font-weight:500;
           color:var(--ink-2); text-decoration:none; min-height:40px; }
  .d-nav:hover { background:var(--surface); color:var(--ink); }
  .d-nav .c-ico { width:18px; height:18px; flex:none; }
  .d-nl { flex:1; min-width:0; }
  .d-on { background:var(--pill-ink); color:var(--pill-text); font-weight:650; }
  .d-tail { font-family:var(--font-mono); font-size:10px; padding:1px 5px;
            border-radius:5px; background:var(--gold-bg); color:var(--gold-dk); }
  .d-on .d-tail { background:var(--surface); color:var(--ink); }
  .d-acct { position:relative; margin-top:auto; padding-top:12px;
            border-top:1px solid var(--line); }
  .d-acct-c { display:flex; align-items:center; gap:9px; padding:9px;
              border:1px solid var(--line); border-radius:var(--r-in);
              background:var(--surface); text-decoration:none;
              cursor:pointer; }
  .d-acct-c:hover { border-color:var(--gold-lt); }
  .d-acct-c .c-ico { flex:none; color:var(--ink-3);
                     transition:transform .16s; }
  #d-ac-o:checked ~ .d-acct-c .c-ico { transform:rotate(180deg); }
  /* UPWARD. It is the last thing in the rail with the viewport floor under
     it, so a menu hanging below it would open off the bottom of the screen.
     Same checkbox the profile switcher uses rather than the prototype's
     :hover, which on a touch screen is a menu that opens and will not close. */
  .d-ac-m { display:none; position:absolute; left:0; right:0;
            bottom:calc(100% + 7px); z-index:40; padding:6px;
            border-radius:var(--r-card); background:var(--surface);
            border:1px solid var(--line);
            box-shadow:0 18px 44px -18px rgba(0,0,0,.5); }
  #d-ac-o:checked ~ .d-ac-m { display:block; }
  .d-ac-x { display:none; }
  #d-ac-o:checked ~ .d-ac-x { display:block; position:fixed; inset:0;
                              z-index:35; }
  .d-ac-i { display:flex; align-items:center; gap:11px; width:100%;
            padding:9px 10px; border:0; border-radius:var(--r-in);
            background:none; cursor:pointer; text-align:left; font:inherit;
            font-size:13px; color:var(--ink); text-decoration:none; }
  .d-ac-i:hover { background:var(--surface-2); }
  .d-ac-i .c-ico { flex:none; color:var(--ink-3); }
  .d-ac-sep { height:1px; background:var(--line); margin:5px 8px; }
  /* No icon, and the only red thing in the rail: it is the one row here that
     does something rather than going somewhere. */
  .d-ac-out { padding-left:37px; color:var(--bad); font-weight:600; }
  @media (prefers-reduced-motion:reduce) {
    .d-acct-c .c-ico { transition:none; }
  }
  .d-av { width:30px; height:30px; border-radius:50%; flex:none;
          display:grid; place-items:center; font-size:12px; font-weight:700;
          background:var(--gold-solid); color:var(--gold-solid-text); }
  .d-who { min-width:0; flex:1; }
  .d-who b { display:block; font-size:13px; color:var(--ink); }
  .d-who i { display:block; font-style:normal; font-size:11.5px;
             color:var(--ink-3); }

  /* ── top bar ── */
  .d-top { display:flex; align-items:center; gap:12px; padding:11px 22px;
           border-bottom:1px solid var(--line); background:var(--cream);
           position:sticky; top:0; z-index:5; }
  .d-crumb { font-size:13px; color:var(--ink-3); min-width:0; }
  .d-crumb b { color:var(--ink); font-weight:600; }
  .d-tb { margin-left:auto; display:flex; align-items:center; gap:8px; }
  .d-chip { display:flex; align-items:center; gap:6px; font-size:12.5px;
            padding:6px 11px; border:1px solid var(--line);
            border-radius:var(--r-pill); background:var(--surface); }
  .d-chip i { font-style:normal; font-family:var(--font-mono); font-size:9.5px;
              letter-spacing:.05em; padding:1px 5px; border-radius:4px;
              background:var(--gold-bg); color:var(--gold-dk); }
  .d-wallet { display:flex; align-items:center; gap:7px; font-size:13px;
              font-weight:700; padding:6px 7px 6px 12px; text-decoration:none;
              border-radius:var(--r-pill); border:1px solid var(--gold);
              background:var(--gold-bg); color:var(--gold-dk); }
  .d-plus { display:grid; place-items:center; width:18px; height:18px;
            border-radius:50%; background:var(--gold-solid);
            color:var(--gold-solid-text); font-size:12px; line-height:1; }
  .d-burger { display:none; }
  .d-scrim { display:none; }

  /* ── main ── */
  .d-main { padding:26px 22px 56px; max-width:var(--wrap); }
  .d-h1 { font-family:var(--font-serif); font-size:clamp(27px,3.2vw,42px);
          line-height:1.08; letter-spacing:-.015em; font-weight:600;
          margin:0 0 9px; text-wrap:balance; }
  .d-h1 em { font-style:italic; color:var(--gold-dk); }
  .d-sub { display:flex; align-items:baseline; gap:8px; margin:0;
           color:var(--ink-2); font-size:14.5px; line-height:1.6;
           max-width:640px; }
  .d-sub b { color:var(--ink); font-weight:650; }
  /* NOT .d-dot. This CSS used to live inside /home/ where a page owned its
     own class names; moving it into lib/ made every name here global, and
     pages/views/biorhythm.py has been drawing its chart's data points with
     a .d-dot for months. The shell is the newcomer, so the shell renames. */
  /* Prose links. Scoped to <p> on purpose: every signed-in page puts the
     odd link in a caption or an empty-state sentence, and without this they
     render browser-default blue in the middle of a gold page. Anchors that
     are structure rather than prose — rail items, rashi tiles, roster cards,
     the profile menu — are never inside a paragraph, so they keep their own
     styling and this cannot reach them. */
  .d-main p a { color:var(--gold-dk); text-underline-offset:2px; }
  .d-main p a:hover { color:var(--gold); }

  .d-live { width:7px; height:7px; border-radius:50%; background:var(--good);
           flex:none; }
  .d-mk { display:flex; align-items:baseline; gap:10px; margin:32px 0 13px; }
  .d-mk0 { margin:0 0 13px; }
  .d-mkn { font-family:var(--font-mono); font-size:12px; color:var(--gold-dk); }
  /* An h2/h3 now, so the browser's own heading margin and 1.5em size have
     to go — the row is a flex baseline and a UA margin would shove it. */
  .d-mkt { font-family:var(--font-mono); font-size:12px; letter-spacing:.13em;
           text-transform:uppercase; font-weight:600; margin:0;
           color:inherit; }
  .d-mkd { font-family:var(--font-serif); font-style:italic; font-size:13px;
           color:var(--ink-3); }
  .d-card { background:var(--surface); border:1px solid var(--line);
            border-radius:var(--r-card); padding:20px; }
  .d-row { display:grid; gap:15px; }
  /* THE LEFT CARD SETS THE HEIGHT AND BEST HOURS FITS ITSELF INTO IT.
     Stretching the pair to their tallest is not the same thing: the tallest
     is whichever has more to say, so the void simply moves to the other
     column — which is exactly what happened when Best hours grew from three
     rows to seven. The slot is an empty positioned box, so it contributes
     nothing to the row; the card fills the slot absolutely and gives up
     rows to its scroller instead of giving up its shape.

     min-height so the pair cannot collapse when the left card is in one of
     its short states — no birth details, or a chart that would not load. */
  @media (min-width:980px) {
    .d-2 { grid-template-columns:1.5fr 1fr; }
    .d-hslot { position:relative; min-height:280px; }
    .d-hslot > .d-hrs { position:absolute; inset:0; }
  }
  /* On one column there is nothing to match, so the card is its own height
     and the whole list is simply there. */
  @media (max-width:979.98px) { .d-hslot { display:contents; } }
  .d-read { font-size:14.5px; line-height:1.65; color:var(--ink-2); margin:0; }
  .d-read b { color:var(--ink); font-weight:650; }

  /* ── profile switcher ── */
  .d-pf { position:relative; }
  .d-pf-t { display:flex; align-items:center; gap:9px; cursor:pointer;
            padding:5px 9px 5px 6px; border:1px solid var(--line);
            border-radius:var(--r-pill); background:var(--surface);
            max-width:230px; }
  .d-pf-t:hover { border-color:var(--gold-lt); }
  .d-pf-n { min-width:0; text-align:left; }
  .d-pf-n b { display:block; font-size:12.5px; font-weight:650; color:var(--ink);
              white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .d-pf-n i { display:block; font-style:normal; font-family:var(--font-mono);
              font-size:10px; color:var(--ink-3); }
  .d-pf-t .c-ico { flex:none; color:var(--ink-3); }
  .d-pf-m { display:none; position:absolute; top:calc(100% + 7px); left:0;
            z-index:40; min-width:250px; padding:6px; border-radius:var(--r-card);
            background:var(--surface); border:1px solid var(--line);
            box-shadow:0 18px 44px -18px rgba(0,0,0,.5); }
  /* The input sits INSIDE .d-top, so the menu and the catcher are its
     siblings there — not descendants of .d-shell, which is what the
     first version reached for and never matched. */
  #d-pf-o:checked ~ .d-pf .d-pf-m { display:block; }
  /* A full-screen catcher so the next click anywhere closes it — without one
     the menu stays open behind whatever you clicked next. */
  .d-pf-x { display:none; }
  #d-pf-o:checked ~ .d-pf-x { display:block; position:fixed;
            inset:0; z-index:35; }
  .d-pf-i { display:flex; align-items:center; gap:9px; width:100%;
            padding:8px 9px; border:0; border-radius:var(--r-in);
            background:none; cursor:pointer; text-align:left;
            text-decoration:none; font:inherit; }
  .d-pf-i:hover { background:var(--surface-2); }
  .d-pf-on { background:var(--gold-bg); }
  .d-pf-on .d-pf-n b { color:var(--gold-dk); }
  .d-pf-add { border-top:1px solid var(--line-soft); border-radius:0 0 9px 9px;
              margin-top:4px; padding-top:11px; }
  .d-pf-add .d-av { background:var(--surface-2); color:var(--ink-2); }
  /* max-width:150px used to sit here too and had never once applied: the
     phone block further down sets max-width:none at the same specificity and
     later in the file, so it won every time. The date of birth is what this
     rule is actually for. */
  @media (max-width:560px) { .d-pf-n i { display:none; } }

  /* ── waiting ── */
  /* A grey block the shape of the answer, not a spinner in the middle of an
     empty page: the layout is already correct, so nothing jumps when the real
     values land. */
  /* A SWEEP, NOT A PULSE. The old rule faded --line-soft in and out, and
     --line-soft sits 1.07:1 against the card it lies on in dark — so the bar
     was invisible at its brightest and more invisible at its dimmest, which
     reads as a page that has finished loading and has nothing to say. The
     band travelling left to right is the part that says "still coming"; the
     bar underneath it is what says "and it will be about this big". */
  .d-sk { display:block; min-height:1em; width:100%; border-radius:6px;
          /* The &nbsp; that gives the span its height must not be seen. */
          color:transparent; user-select:none;
          background-color:var(--sk);
          background-image:linear-gradient(100deg, rgba(0,0,0,0) 34%,
                           var(--sk-hi) 50%, rgba(0,0,0,0) 66%);
          background-size:280% 100%; background-repeat:no-repeat;
          animation:d-sheen 1.45s linear infinite; }
  .d-sk + .d-sk { margin-top:7px; }
  @keyframes d-sheen { from { background-position:170% 0; }
                       to { background-position:-70% 0; } }
  /* Stationary, not still-but-invisible: reduced motion keeps the bar and
     drops only the travel. */
  @media (prefers-reduced-motion:reduce) {
    .d-sk { animation:none; background-image:none; }
  }

  /* IN A FLEX PARENT A SKELETON COLLAPSES. .d-sub is a flex row, so a
     `<span class="d-sk">&nbsp;</span>` shrink-wrapped its one space and came
     out 3px wide — five screens opened with a crumb of grey under the
     headline where a line of type was about to appear. Width here, and a
     shorter one for the sub, because a full-column bar promises a paragraph
     the line will not deliver. */
  .d-sub .d-sk { flex:0 0 auto; width:min(26ch, 100%); }

  /* Seen only when a live reading could not be fetched and the page is
     showing what was built into it. It is a caveat on real data, not an
     error — amber, in the subtitle, not a banner across the page. It lives
     here because every signed-in page that reads a dated snapshot needs the
     same sentence, and three copies would drift. */
  .d-stale { color:var(--mid); font-size:13px; }
  .d-stale b { color:var(--mid); font-weight:650; }
  /* Same collapse, in the top bar. The profile chip and the wallet pill both
     size to their contents, so a width:100% child resolves against nothing —
     3px and 13px respectively. These are the widths a name and a balance
     actually take, so the chrome does not resize when they arrive. */
  .d-pf-n .d-sk { width:9ch; }
  .d-wallet .d-sk { width:4.5ch; }

  /* ── the rail becomes a drawer ── */
  @media (max-width:900px) {
    .d-shell { grid-template-columns:minmax(0,1fr); }
    .d-burger { display:grid; place-items:center; width:44px; height:44px;
                flex:none; cursor:pointer; border:1px solid var(--line);
                border-radius:10px; background:var(--surface);
                color:var(--ink-2); }
    .d-rail { position:fixed; top:0; left:0; bottom:0; width:264px; z-index:40;
              transform:translateX(-100%); transition:transform .2s ease; }
    #d-menu:checked ~ .d-shell .d-rail { transform:none; }
    #d-menu:checked ~ .d-shell .d-scrim { display:block; position:fixed;
              inset:0; z-index:30; background:rgba(0,0,0,.45); }
    .d-main { padding:20px 16px 48px; }
    .d-top { padding:10px 16px; }
  }
  /* ── ONE ROW ON A PHONE ──
     The bar holds a burger, the profile chip, a breadcrumb, a date, a wallet
     and the theme toggle: 473px of controls in 358px at 390. Letting it wrap
     kept everything on screen and made the header 116px of a 844px phone —
     two rows, the second one shoved to the right by margin-left:auto with
     124px of dead space beside it. It read as a header that had come apart,
     not one that had been designed.
     Two things go instead of wrapping. The breadcrumb, because it names the
     page a reader is looking at. And the date chip, because it is the only
     thing on the bar that is not a control — every page that turns on the
     date says so in its own heading, and what a phone needs up here is the
     way out (burger), who this is cast for (profile), and the money.
     What is left is 191px of fixed controls, so the profile takes the rest
     and truncates. That is why .d-pf gets min-width:0: it is a flex item, its
     min-width defaults to its content, and without this the name would push
     the wallet off the edge rather than ellipsing — the same trap that gave
     the roster card a 326px floor. */
  @media (max-width:620px) {
    .d-top { flex-wrap:nowrap; gap:8px; }
    .d-crumb { display:none; }
    .d-chip { display:none; }
    .d-tb { margin-left:auto; flex:none; gap:8px; }
    /* FITS ITS NAME, and shrinks only when the row runs out. flex defaults to
       0 1 auto, so the chip is as wide as its contents and no wider; the
       max-width is the ceiling a long name truncates at, and min-width:0 is
       what lets it give way at 320 rather than widening the page.
       It used to be flex:1 1 auto with width:100%, which stretched it across
       whatever the bar had spare — a chip holding "Rohit Singh" sat 167px
       wide with the name floating in the left third of it. */
    .d-pf { min-width:0; }
    .d-pf-t { max-width:190px; }
  }
  @media (prefers-reduced-motion:reduce) { .d-rail { transition:none; } }
  /* ── 11px IS THE FLOOR ON A PHONE, EVERYWHERE INSIDE THE SHELL ──
     Thirty-six rules across four files set text between 8.5px and 10.5px on
     these pages: axis labels, table headings, row labels, the segment names
     inside the dasha bars, date pills, the notification-time caption. Each was
     a fair call at 1280px, where the same component has half again the room.
     On a phone they are collectively most of the words on the screen, and
     8.5px is not a size anybody reads on a bus.
     ONE BLOCK, NOT THIRTY-SIX EDITS, because scattering the same override
     through four files is how the thirty-seventh gets written at 9px and
     nobody notices. .d-main prefixes every selector because page CSS is
     emitted AFTER the shell's, so this needs specificity rather than position
     to win — and from here it wins wherever the base rule lives.
     Anything that genuinely cannot hold 11px gets room made for it instead of
     being allowed back under the floor.
     THREE OF THESE WERE INVISIBLE LOCALLY. .d-fx (the dashboard's tithi /
     nakshatra / yoga / karana row) and .c-tl-now (the "now" marker on the day
     timeline) only render when there is live panchang data, and a local build
     runs with COSMIQE_STALE_OK=1. They were found by running the check
     against production after deploying, which is now the only way to see
     them — worth remembering before trusting a local pass on these pages. */
  @media (max-width:640px) {
    .d-main .c-lb-i i,
              .d-main .c-pf-l,
              .d-main .c-scl-l,
              .d-main .c-scl-t,
              .d-main .c-scl-tag,
              .d-main .c-scl-v,
              .d-main .c-table th,
              .d-main .c-tl-c b,
              .d-main .c-tl-lbl,
              .d-main .c-tl-night i,
              .d-main .c-tl-now i,
              .d-main .d-fx s,
              .d-main .d-fx i,
              .d-main .c-tl-t i,
              .d-main .d-flc s,
              .d-main .d-lk s,
              .d-main .d-ovl,
              .d-main .d-rate,
              .d-main .d-scl,
              .d-main .dl-lab i,
              .d-main .dl-lab s,
              .d-main .dl-life-t,
              .d-main .dl-ls b,
              .d-main .dl-ls i,
              .d-main .dl-seg,
              .d-main .dl-sg-h s,
              .d-main .dl-sg-r s,
              /* .dl-tbl th was here. The dasha table stacks on a phone and
                 hides its heads, so there is no width at which this rendered
                 — see the block at the end of dasha_app.py. */
              .d-main .h-dl-r .h-dl-pc,
              .d-main .h-f-b u,
              .d-main .h-f-bn,
              .d-main .h-f-hint,
              .d-main .h-f-l,
              .d-main .h-f-luck-t,
              .d-main .h-f-tile i,
              .d-main .h-trend,
              /* ── THE THREE THAT MIX THEIR OWN TEXT WITH A CHILD ──
                 Missed for a reason in the checks rather than in the page:
                 shell-mobile.mjs only measured elements with no element
                 children at all, so anything shaped like
                 <text>Finance<tspan>87</tspan></text> or
                 <span><svg/>Tithi<i>…</i></span> was never looked at, on five
                 horoscope pages and panchang. The eyebrow is 10.5, the anga
                 heading 10, and the radar label 8.5 in an SVG that renders it
                 at 9.0 on a 320 screen. */
              .d-main .h-f-eyebrow,
              .d-main .h-radar-l,
              .d-main .p-anga-h,
              .d-main .p-ch-h,
              .d-main .p-per-h,
              .d-main .p-sum i,
              .d-main .st-pill-off i { font-size:11px; }
  }

  /* Own prefix rp-. .c-rp is the shared report card and is not restyled. */
  .rp-note { font-size:13.5px; line-height:1.62; color:var(--ink-2);
             max-width:74ch; margin:0 0 16px; }
  .rp-note b { color:var(--ink); font-weight:650; }
  /* ── the shelf ── */
  .rp-row { display:grid; gap:4px 15px; align-items:center;
            grid-template-columns:1fr; padding:15px 17px; margin-bottom:11px;
            border:1px solid var(--line); border-radius:14px;
            background:var(--surface); }
  @media (min-width:780px) {
    .rp-row { grid-template-columns:minmax(0,1fr) auto auto;
              grid-template-rows:auto auto; }
    .rp-row b { grid-area:1 / 1; }
    .rp-row .rp-when { grid-area:2 / 1; }
    .rp-row .rp-st { grid-area:1 / 2 / span 2 / 3; }
    .rp-row .rp-act, .rp-row .rp-acts { grid-area:1 / 3 / span 2 / 4; }
  }
  .rp-row b { font-family:var(--font-serif); font-size:17px; font-weight:600; }
  .rp-when { font-size:12.5px; color:var(--ink-3); }
  .rp-st { font-family:var(--font-mono); font-size:10px; letter-spacing:.1em;
           text-transform:uppercase; padding:4px 10px;
           border-radius:var(--r-pill); justify-self:start; }
  .rp-st-ok { background:var(--good-bg); color:var(--good); }
  .rp-st-mid { background:var(--mid-bg); color:var(--mid); }
  .rp-st-bad { background:var(--bad-bg); color:var(--bad); }
  /* They are <button>s now rather than links — see the note in the row
     builder — so the browser's own font, border and background have to be
     turned off before this rule means anything. */
  .rp-act { justify-self:start; font:inherit; font-size:13px; font-weight:600;
            padding:9px 15px; border-radius:var(--r-in); border:0;
            cursor:pointer; white-space:nowrap;
            background:var(--gold-solid); color:var(--gold-solid-text);
            text-decoration:none; }
  /* Two actions, one row. View leads: a reader who has just paid wants to
     SEE it, and saving is what they do second if at all. */
  .rp-acts { justify-self:start; display:flex; gap:8px; flex-wrap:wrap; }
  .rp-acts .rp-act { justify-self:auto; }
  /* The second is the quieter one — one gold button per row, so the eye is
     not asked to choose between two identical claims on it. */
  .rp-act-2 { background:var(--surface-2); color:var(--ink);
              box-shadow:inset 0 0 0 1px var(--line); }
  .rp-act-2:hover { background:var(--gold-bg);
                    box-shadow:inset 0 0 0 1px var(--gold); }
  .rp-act[disabled] { opacity:.65; cursor:default; }
  /* The reports a purchase just produced, under the line that says so. */
  /* ── who it is going to ── */
  .rp-mail { margin:0 0 11px; font-size:12.5px; line-height:1.5;
             color:var(--ink-3); }
  .rp-mail[hidden] { display:none; }
  /* [hidden] BEATEN BY A CLASS, TWICE ON THIS ROW. `display:block` here and
     `display:inline-flex` on .rp-mail-go in the touch block below are class
     rules; the hidden attribute is a UA rule at lower specificity, so
     `at.hidden = true` and `go.hidden = true` both did nothing and the row
     rendered "We'll email it to <old address>", a Change button and the
     edit field all at once. Same failure ui.py:526 records for
     /onboarding/'s Try again button. */
  .rp-mail-at[hidden], .rp-mail-go[hidden] { display:none; }
  .rp-mail-at { display:block; color:var(--ink); font-weight:600;
                overflow-wrap:anywhere; }
  .rp-mail-go { font:inherit; font-size:12px; font-weight:600; padding:0;
                margin-top:2px; border:0; background:none; cursor:pointer;
                color:var(--gold-dk); text-decoration:underline;
                text-underline-offset:2px; }
  .rp-mail-go:hover { color:var(--ink); }
  .rp-mail-ed[hidden] { display:none; }
  .rp-mail-ed { display:block; }
  .rp-mail-in { width:100%; font:inherit; font-size:13px; margin-top:4px;
                padding:8px 10px; border-radius:var(--r-in);
                border:1px solid var(--line); background:var(--surface);
                color:var(--ink); }
  .rp-mail-in:focus-visible { outline:2px solid var(--gold-dk);
                              outline-offset:1px; }
  .rp-mail-no { display:block; font-style:normal; margin-top:5px;
                color:var(--bad); }
  .rp-mail-no[hidden] { display:none; }
  .rp-got { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }
  .rp-got .rp-act { font-size:12.5px; padding:7px 12px; }
  /* IN THE PURCHASE PANEL THEY ARE THE PAIR, NOT AN AFTERTHOUGHT. On the
     shelf a row is a report and these two sit at the end of it, small,
     because the row is the subject. In the panel the reader has just paid
     and these ARE the subject: Download came out at 12.5px and 7px of
     padding under a 48px button, which read as a footnote to the thing it
     is half of. Same height, same radius, same weight as .rp-payb; the
     fill is what separates them, because opening it is the one to press
     and keeping it is the one to press later. */
  .rp-pay .rp-got { display:grid; gap:8px; margin-top:12px; }
  /* IT HAS TO BE ALLOWED TO WRAP, AND width:100% DOES NOT DO THAT. .rp-act
     carries white-space:nowrap for the shelf, where these are two small
     chips at the end of a row and a wrapped "Download" would be silly. In
     this panel each button names its report — "Download Kal Sarpa Dosha" —
     and nowrap makes the button's min-content the whole unbroken label. A
     grid item's min-width is `auto`, which resolves to min-content and
     OVERRIDES width:100%: measured at 1440px, a 212px panel held five 256px
     buttons, 45px past its own right edge. Not clipped — sticking out, which
     is why it read as a broken card rather than a truncated word.
     min-width:0 lets the item shrink to its track and white-space:normal
     gives it somewhere to put the rest. Two lines on a 244px column is the
     honest answer; the alternative is dropping the report's name, and then
     five identical buttons name nothing. */
  .rp-pay .rp-got .rp-act { display:flex; align-items:center;
      justify-content:center; gap:9px; width:100%; min-width:0;
      min-height:48px; white-space:normal; text-align:center;
      line-height:1.25; text-wrap:balance;
      padding:9px 14px; border-radius:10px; font-size:14.5px; font-weight:700;
      background:transparent; color:var(--gold-dk);
      border:1px solid var(--gold); }
  .rp-pay .rp-got .rp-act:hover { background:var(--gold-bg); }
  /* See the markup in finish(): the report's name is its own line. Slightly
     smaller and not bold, because "Download" is the action and the title is
     which one — two things, not one long shout. */
  .rp-pay .rp-got .rp-act i { display:block; font-style:normal;
      font-size:13.5px; font-weight:600; }
  /* The icon rides both. A verb plus a shape is read faster than a verb,
     and these two verbs — open, keep — are exactly the pair a reader
     mixes up when both are plain words on stacked buttons. */
  .rp-payb svg, .rp-pay .rp-got .rp-act svg { flex:none; }
  /* THE LABEL RECENTRES WHEN THE PRICE LEAVES. .rp-payb is space-between
     so the figure sits at the far end of the ask; once it is paid, landed()
     hides the figure, and space-between then held the icon against the left
     edge and the label against the right with 200px of gold between them.
     BOTH CLASSES IN THE SELECTOR: .rp-payb-paid alone is 0-1-0, the same as
     .rp-payb, which is declared further down this stylesheet and therefore
     won on order — measured, the button stayed split. */
  .rp-payb.rp-payb-paid { justify-content:center; }
  /* Still the button, still the same size, but it is not an offer any more
     while the thing behind it is being written. */
  .rp-payb-done { opacity:.72; cursor:default; }
  .rp-act-off { background:var(--surface-2); color:var(--ink-3);
                box-shadow:inset 0 0 0 1px var(--line); }
  .rp-empty { font-size:13.5px; line-height:1.62; color:var(--ink-2);
              max-width:74ch; margin:0; }
  .rp-sk-row { height:78px; min-height:78px; border-radius:14px;
               margin-bottom:11px; }

  /* ── one report ── */
  /* .d-sub sits flush under the h1 with no margin of its own — that is right
     for a page whose next thing is a section mark, and too tight when the
     next thing is a card. Owned here rather than in the shell, which every
     other app page shares. */
  /* ── AND THE WIDTH THAT DECIDES IS THIS COLUMN'S, NOT THE WINDOW'S ──
     These were viewport media queries at 820 and 1240, and the signed-in
     copy of this page carries a 265px rail the public copy does not. So at
     a 1298px window — a perfectly ordinary laptop — the rule fired, the
     330px card took its place beside the hero, and the hero was left with
     638px to hold a cover, two bullet lists and the purchase panel. The
     lists came out one word per line: "Your / birth / date, / time and /
     place". The same page at the same window width was fine signed out.

     A media query cannot see the rail. .d-main is what .rp-top actually
     lives in on both copies, so that is what is asked, and the thresholds
     below are its content width rather than a window's. Second time this
     file has had the relationship backwards — see .rp-buyer-x, where the
     widest window has the narrowest panel. */
  .rp-main { container-type:inline-size; }
  .rp-top { display:grid; gap:18px; grid-template-columns:1fr;
            margin:20px 0 8px; }
  /* THREE THINGS, AND THE WIDTH DECIDES HOW THEY SIT. The hero used to be
     art beside one column of prose, so at 1440 a third of the widest part of
     the page held nothing — the lede wraps at 60ch and everything under it is
     left-aligned. Narrow: stacked. Middle: art beside the pitch, the case for
     the report spanning underneath, because a 330px third column would leave
     the pitch at 230px. Wide: all three in a row. */
  @container (min-width:776px) {
    /* STRETCH, NOT START. Three blocks that each ended wherever their content
       ran out left the tallest one — always the cover — with a hand's depth of
       nothing beside it. Same height, and each one distributes its own slack:
       the pitch pins its price block to the floor, the panel centres. */
    /* Two children now, not three: the hero card and the panel beside it.
       Stretch, so the panel is as tall as the card rather than ending where
       its four sentences run out. */
    .rp-top { grid-template-columns:minmax(0,1fr); gap:20px 26px;
              align-items:stretch;
              grid-template-areas:'hero' 'short'; }
    .rp-hero { grid-area:hero; }
    .rp-short { grid-area:short; }
    /* Only the bullets go two-up. The price list has a ruled Separately /
       Together at the bottom, and splitting it puts the total beside the
       subtotal instead of under it. */
    .rp-short .rp-highs { grid-template-columns:1fr 1fr; column-gap:30px; }
  }
  /* 1190 IS MEASURED, NOT DERIVED. The side card takes 330 plus a 26px gap,
     so the hero gets the column minus 356 — and the sweep says the two
     bullet lists inside it hold one line each down to a hero of 840, break
     to two at 780, and to three at 712, where the narrowest item is 150px:
     "Your / birth / date, / time and / place", which is what the reader
     photographed. 840 + 356 = 1196, so the card only sits beside the hero
     from 1190 of column up. Below that it goes underneath, which is where
     it was at every width below 1240 before this anyway. */
  @container (min-width:1190px) {
    .rp-top { grid-template-columns:minmax(0,1fr) minmax(0,330px);
              grid-template-areas:'hero short'; }
    .rp-short .rp-highs { grid-template-columns:1fr; }
  }
  .rp-short { padding:17px 19px; }
  /* <s> MEANS "NO LONGER ACCURATE". These are labels — "What it tells
     you", "Reports", "Sections" — and a screen reader that announces
     strikethrough told the reader every one of them had been withdrawn.
     They were only ever <s> because of the `text-decoration:none` right
     here, which is the tell: an element chosen for its look and then made
     not to look like itself. The one real <s> left on the page is .rp-mrp,
     the price an offer replaces, which genuinely is no longer accurate. */
  .rp-short > span { display:block; text-decoration:none;
                  font-family:var(--font-mono); font-size:9.5px;
                  letter-spacing:.13em; text-transform:uppercase;
                  color:var(--ink-3); margin-bottom:12px; }
  .rp-short .rp-highs li { max-width:none; font-size:13px; }
  /* The art at the size it was drawn. Portrait, so the card is portrait —
     the catalogue's round 78px well is what makes these look like icons. */
  /* No padding and no centring: there is nothing left to centre once the
     cover is the width of the column. */
  /* THE COVER IS THE PANEL. .d-card brings 20px of padding and a border, and
     around a book cover that padding is the "empty white box" this was
     reported as — 20px of nothing on all four sides of the one picture that
     is the product. The card keeps its border and its corner and clips the
     art to them; the art fills what is left. The bundle panel below is the
     exception and keeps its padding, because it holds a fan of small covers
     rather than one big one. */
  .rp-art:not(.rp-art-b) { display:grid; padding:0; overflow:hidden; }
  .rp-art:not(.rp-art-b) .rp-cover { border-radius:0; box-shadow:none; }
  /* Top-heavy on purpose: the lede reads first, the price block sits on the
     same floor as the bottom of the cover. */
  .rp-say { display:flex; flex-direction:column; }
  /* THE RULE SEPARATES THE FACTS FROM THE ACT. It used to sit above the
     Sections / Format / Price row, which cut the description off from the
     numbers describing the same thing. */
  /* NOT margin-top:auto ANY MORE. Pinning the buy block to the floor lined it
     up with the bottom of a cover that was capped at 318px; the cover is a
     full-height poster now, so the same rule opened a 200px hole in the
     middle of the column — between the sample button and the rule — with the
     slack landing where the reader is still reading. Top-packed, the same
     slack sits under the last line instead, which is just margin. */
  /* .rp-act-b AND .rp-buy ARE GONE with the bundle's lone gold button —
     both pages take the money through .rp-pay now, which brings its own
     border and its own rule. */
  .rp-short { display:flex; flex-direction:column; }
  /* IT IS ITS OWN HEIGHT NOW, AND THE CENTRING IS GONE WITH THE STRETCH.
     .rp-top aligns stretch so the two columns end level, which was right
     when they were close in height. They are not: measured at 1600 the card
     is 680px around 193px of bullets, and `margin-block:auto` split the
     other 487 evenly — 257px of nothing between the label and the first
     bullet and 230 under the last. A heading with a screen's worth of gap
     beneath it reads as a panel that failed to load, which is the exact
     failure the note below this one records for the bundle's price list at
     a THIRD of that gap.
     So the card ends where its content ends. A short card beside a tall one
     is a sidebar; a tall card with its middle empty is a bug. */
  .rp-short { align-self:start; }
  /* THE COVER FILLS ITS COLUMN. It did not, and the reason was that nothing
     ever asked it to grow: `width:auto; max-width:100%` caps an image and
     never scales one up, so the browser painted the 126px cut at its natural
     126x178 inside a 270x283 box — 144px of slack across and 105 down, a
     stamp floating in a panel. It is the thing being sold and it was the
     smallest thing on the page.
     Height no longer leads. The cap it used to carry was there because the
     cover set the height of the whole band, which is now the point: this is
     a book cover and a book cover is a poster, so the band takes its depth
     from it and the two columns beside it distribute their own slack (they
     already do — .rp-say pins its price block to the floor, .rp-short
     centres). */
  /* THE ART TOUCHES THE TOP OF THE CARD. object-fit:contain letterboxes the
     poster inside a column whose height comes from the text beside it, and
     the default `50% 50%` split that slack evenly — so the cover floated,
     with a band of nothing above it as well as below. Whatever slack is
     left now pools at the bottom, in one place, where it reads as the end
     of the column rather than as the image having failed to reach the top. */
  .rp-cover { display:block; width:100%; height:100%; border-radius:10px;
              /* cover, not contain: the art column is as tall as the band and
                 the band is set by the text beside it, so the two shapes never
                 quite agree. contain would letterbox the difference back into
                 the empty space this is fixing; cover spends it on the bottom
                 edge, which on all ten of these is border and no title. */
              object-fit:cover; object-position:top;
              box-shadow:0 14px 34px rgba(40,26,6,.20); }
  .rp-cover-x { display:grid; place-items:center; width:100%; height:280px;
                color:var(--ink-3); }
  /* ── THE HERO IS ONE CARD ──────────────────────────────────────────
     Cover and case inside a single border, the cover flush to its left edge
     and clipped to its corner. It was two blocks with a gap between them —
     a bordered picture, then loose text — which reads as an illustration
     beside a paragraph rather than as one thing being sold. */
  .rp-hero { display:grid; grid-template-columns:1fr; padding:0;
             overflow:hidden; }
  @media (min-width:640px) {
    .rp-hero { grid-template-columns:236px minmax(0,1fr); }
    /* ── THE ART BLOCK IS THE ART, AND STOPS THERE ──────────────────
       .rp-art is tinted (--surface-2) and was a grid item stretched to
       the row, while the poster inside it kept its 520:735 and could only
       fill as much as its width allowed. Everything left over was tinted
       column: 89px under the cover at 1180, 287 at 768. Pushing the
       artwork to the top of that box only moved the hole; it did not
       close it, and a coloured block with a hole in it reads as a panel
       that failed to load.
       start, and the image at its own height: the block ends where the
       poster ends and what follows is card. Nothing to strand, at any
       width, because there is no box left over to strand it in. */
    .rp-hero { align-items:start; }
    .rp-hero .rp-cover { height:auto; }
    /* AND IT DROPS BY THE CARD'S OWN PADDING. The artwork bled to the top
       edge while the text and the panel began 24px down, where .rp-say's
       padding puts them — so the three columns started at +1, +25 and +25,
       and the cover read as having slipped upward out of its row rather
       than as a deliberate bleed. The text cannot come up to meet it: 24px
       is the card's padding and moving it would only make the artwork
       align with a row that no longer looked like a card.
       Left and bottom still bleed, which is the part worth keeping — the
       poster runs off the edge of the block it sits in. */
    .rp-hero .rp-art { margin-top:24px; }
  }
  .rp-hero .rp-art { padding:0; overflow:hidden; background:var(--surface-2); }
  .rp-hero .rp-say { padding:24px 26px 22px; }
  /* THE WHOLE COVER, NOT AS MUCH OF IT AS FITS. This was object-fit:cover
     at height:100%, on the reasoning that a book cover is a poster and the
     band should take its depth from it. What it actually did was let the
     TEXT beside it set the depth and then crop the painting to match: at
     236x450 a 520x735 cover loses a quarter of its width, and on Varshphal
     that is the "V" and the "L" of the title — the one picture of the thing
     being sold, with its name cut off at both ends.
     contain, and the column's own surface behind it. The letterboxing that
     argument was avoiding is now a band of the panel colour above and below
     a whole cover, which is a margin; a cropped title is a defect. */
  /* TOP, NOT CENTRE. contain letterboxes the poster inside a column whose
     height comes from the text beside it, and `center` split that slack
     evenly — so the artwork floated with a band of nothing above it as well
     as below, measured at 46px above at 1180 and 1298. Whatever slack is
     left now pools at the bottom, in one place, and the cover starts where
     the card does.
     WRITTEN HERE AND NOT ON .rp-cover, because that is 0-1-0 and this is
     0-2-0: the first attempt set it on the bare class, the browser kept
     `50% 50%`, and the measurement said 46px when the CSS said top. Third
     specificity loss in this file today. */
  .rp-hero .rp-cover { width:100%; height:100%; border-radius:0;
                       box-shadow:none; object-fit:contain;
                       object-position:center top; }
  @media (max-width:639px) {
    .rp-hero .rp-cover { height:auto; max-height:340px; }
  }
  /* ── THE PHONE IS NOT A NARROW DESKTOP ────────────────────────────────
     Measured before touching it, at 390px: the hero was 935px tall — taller
     than the screen — the page ran 4,280px, and the buy button sat 1,001px
     down. On desktop the cover, the case for the report and the price are
     three columns side by side, so a reader sees all three at once; stacked
     into one column that becomes a tower with the price at the bottom, which
     is the one thing somebody deciding on ₹299 wants first.

     THREE CHANGES, IN ORDER OF HOW MUCH THEY MATTER.

     1. THE PANEL COMES SECOND, not last. order: on the flex column puts the
        price directly under the cover and pushes the two lists below it —
        the lists are the detail you read AFTER you know the number, and on
        desktop they are beside it rather than before it, so this is the
        same reading order rather than a different one.
     2. THE COVER GIVES UP HEIGHT. 340px of art before a word of the offer
        is a third of the screen; 210 still reads as a poster.
     3. AND THE PRICE FOLLOWS YOU DOWN. Everything below the fold — the
        pages, the eighteen sections, who it is for — is what convinces
        somebody, and reaching the end of it used to mean scrolling back up
        past all of it to act. See .rp-bar. */
  @media (max-width:639px) {
    .rp-hero .rp-say { padding:18px 18px 20px; display:flex;
                       flex-direction:column; }
    /* ── THE COVER FILLS THE CARD ──────────────────────────────────────
       It was `contain`, which fits the whole cover into a full-width box
       and pays for it in cream: on a phone the art sat in the middle with
       forty pixels of panel down each side, floating rather than being the
       top of the card.
       Full width and cropped from the BOTTOM, which is the crop these
       covers can take. The earlier version of this cropped horizontally —
       a 124px column out of a 520px painting — and cut the title off at
       both ends, which is what "make it fit properly" was about. Every one
       of these ten puts its title in the top third and its border and the
       CosmiqeAI mark at the foot, so object-position:top keeps the whole
       name and gives up the part that carries no information. */
    .rp-hero .rp-art { border-radius:0; }
    /* NO CROP AT ALL. Cropping to 300px filled the card and cut the artwork
       through the middle of the mandala — the title survived and the picture
       did not, which is worse than the cream bands it replaced. These are
       posters at 520x735 and the whole point of one is that it is whole.
       Full width, natural ratio, nothing lost: 358x506 on a 390px phone.
       IT IS THE STICKY BAR THAT PAYS FOR THIS. A hero this tall would
       normally push the price off the screen, but .rp-bar shows the moment
       the panel is not visible — which at the top of the page is
       immediately — so the number is on screen from the first paint and the
       cover gets to be the size it deserves. */
    .rp-hero .rp-cover { width:100%; height:auto; max-height:none;
                         object-fit:contain; }
    .rp-lede { font-size:14.5px; margin:0 0 14px; }
    /* The panel out of .rp-give and up, directly under the lede. */
    .rp-give { display:contents; }
    .rp-give > div { order:2; margin-top:18px; }
    .rp-give > .rp-pay { order:1; }
    .rp-tick li { font-size:13.5px; margin:5px 0; }
    .rp-pay { padding:14px 14px 16px; }
  }

  /* ── THE BAR THAT FOLLOWS ──────────────────────────────────────────────
     Only on a phone, and only once the real panel has gone off the top —
     two controls saying the same thing on one screen is a choice nobody
     asked for. It carries data-rp-buy, so buy_js binds it with the panel's
     own button and it settles into the same states without knowing any of
     them: `btns` is a querySelectorAll and label() already loops.
     env(safe-area-inset-bottom) because the home indicator sits over the
     bottom edge on every iPhone since the X. */
  .rp-bar { position:fixed; left:0; right:0; bottom:0; z-index:60;
            display:none; align-items:center; gap:12px;
            padding:10px 14px calc(10px + env(safe-area-inset-bottom));
            background:var(--surface); border-top:1px solid var(--line);
            box-shadow:0 -6px 22px rgba(0,0,0,.13); }
  .rp-bar-t { flex:1 1 auto; min-width:0; font-size:12px; line-height:1.35;
              color:var(--ink-3); }
  .rp-bar-t b { display:block; font-size:13.5px; color:var(--ink);
                font-weight:650; white-space:nowrap; overflow:hidden;
                text-overflow:ellipsis; }
  .rp-bar .rp-payb { width:auto; flex:0 0 auto; min-height:46px;
                     padding:0 18px; gap:14px; }
  @media (max-width:639px) {
    .rp-bar.is-on { display:flex; }
    /* The bar covers the last of the page otherwise, and the last thing on
       this page is the way back to the shelf. */
    .rp-bar.is-on ~ .d-main, body:has(.rp-bar.is-on) .d-main,
    body:has(.rp-bar.is-on) main { padding-bottom:84px; }
  }
  @media (prefers-reduced-motion:reduce) { .rp-bar { transition:none; } }

  /* ── give and get ──────────────────────────────────────────────────
     Two sub-columns under the lede: what the reader hands over, what comes
     back, and the block that takes the money beside them. The buy panel is
     a fixed 244px so the price never sets the width of anything — it ships
     hidden behind a shimmering bar and a panel that resized when the figure
     landed would be the same flicker in a new place. */
  /* TOP, WITH THE ARTWORK. This was centred for a while, on the argument
     that the panel is always the taller of the two columns and the prose
     ending a third of the way down left the rest empty. Measured, centring
     put the prose 49px below the top of the card while the cover beside it
     started at 1 and the panel at 25 — three columns beginning at three
     different heights, which is a worse thing to look at than slack at the
     bottom of one of them. All three start together now. */
  /* NO margin-top. It was the gap under the pitch, back when the pitch was
     a full-width row above this. The pitch is the first thing INSIDE the
     left column now, so the margin was 18px of nothing along the top of the
     whole row — most visibly above the purchase panel, which has no heading
     to explain it. */
  .rp-give { display:grid; grid-template-columns:minmax(0,1fr); gap:22px;
             align-items:start; }
  /* THE HERO'S OWN WIDTH, for the third time in this file. .rp-give puts
     the purchase panel beside the two bullet lists, and it was doing it on
     a 1100px WINDOW — which on the signed-in copy, behind a 265px rail and
     with the side card already taken out, is a hero of 808px, and the
     lists went to two lines again inside it. The hero is what has to be
     wide enough, so the hero is what is asked: 244 for the panel, 28 of
     gap and the sweep's 840 floor for the lists is 1112, and the hero is
     its card's full width less 2px of border. */
  .rp-hero { container-type:inline-size; }

  /* ── THE MIDDLE BAND KEEPS TWO COLUMNS, WITH A WIDER COVER ────────
     Between a hero of 580 and 800 there is not room for three columns: the
     cover takes its share, the panel wants 244, and what is left for the
     prose is 172 — measured, with every bullet on two lines. So this band
     is two columns, and the second one stacks:

         ┌────────────┬──────────────────┐
         │            │  pitch           │
         │   cover    │  the two lists   │
         │            │  see a sample    │
         │            │                  │
         │            │  ┌────────────┐  │
         │            │  │ buy panel  │  │
         └────────────┴──┴────────────┴──┘

     The cover column goes to 300 from 236 — at 580 of hero that still
     leaves the right column 280, and the extra 64 is what stops the
     artwork being a thin strip beside a card wider than itself.

     The panel sits at the BOTTOM of its column rather than directly under
     the prose: .rp-say is already a flex column, so space-between pins it
     to the floor and any slack opens between the reading and the paying,
     which is the one place on this block where a gap is not a defect.

     Above 800 nothing changes: three across, 425px tall. Below 580
     nothing changes either: one column, stacked. */
  @container (min-width:580px) and (max-width:799.98px) {
    /* FLUID, NOT 300 FLAT. A flat 300 is right at the top of this band and
       wrong at the bottom: at a hero of 608 it leaves the prose 256px and
       "English, with the Sanskrit in Devanagari" goes to two lines. 40%
       tracks the hero — 243 at 608, 294 at 736 — and the clamp keeps it
       between the 236 the wide layout uses and the 300 that stops the
       artwork being a strip. */
    .rp-hero { grid-template-columns:clamp(236px,40%,300px) minmax(0,1fr); }
    .rp-hero .rp-say { justify-content:space-between; }
    /* Not the full 408 of the column: a purchase panel goes weak and sparse
       stretched much past 380, and the slack is better spent as margin than
       as a wider gold button. */
    .rp-hero .rp-pay { max-width:380px; }
  }
  /* THE HERO'S OWN WIDTH, for the third time in this file. .rp-give puts
     the purchase panel beside the two bullet lists, and it was doing it on
     a 1100px WINDOW — which on the signed-in copy, behind a 265px rail and
     with the side card already taken out, is a hero of 808px, and the
     lists went to two lines again inside it. The hero is what has to be
     wide enough, so the hero is what is asked: 244 for the panel, 28 of
     gap and the sweep's 840 floor for the lists is 1112, and the hero is
     its card's full width less 2px of border. */
  .rp-hero { container-type:inline-size; }

  /* 800 IS MEASURED. The panel needs 244 and its gap 28; the rest is the
     prose. Injected five candidates into a live page at six widths and read
     the damage back:

       give>=   1024        1180        1298        1440        1640
       1110     pay 442     pay 598     pay 716     pay 244     pay 670
       900      pay 442     pay 598     pay 244     pay 244     pay 244
       800      pay 442     pay 244     pay 244     pay 244     pay 244
       720      pay 244*    pay 244     pay 244     pay 244     pay 244

     pay is the purchase panel's width: 244 means it sits beside the prose,
     anything larger means it fell underneath and stretched to the hero. At
     1110 that produced a 716px-wide gold button on an ordinary laptop, and
     the hero went to 680px tall to hold it, which is where the empty column
     beside the cover came from. 720 fixes the last width too and is marked
     * because the bullets go to two lines there — so 800. */
  @container (min-width:800px) {
    .rp-give { grid-template-columns:minmax(0,1fr) 244px; gap:28px; }
  }
  .rp-lbl { display:block; text-decoration:none; font-family:var(--font-mono);
            font-size:9.5px; letter-spacing:.13em; text-transform:uppercase;
            color:var(--ink-3); margin:0 0 8px; }
  .rp-tick { margin:0 0 17px; padding:0; list-style:none; }
  .rp-tick li { position:relative; padding-left:19px; font-size:14px;
                line-height:1.55; color:var(--ink-2); margin:6px 0;
                max-width:46ch; }
  .rp-tick li::before { content:''; position:absolute; left:0; top:8px;
                        width:7px; height:7px; border-radius:50%;
                        background:var(--gold); }

  /* ── the purchase block ── */
  .rp-pay { border:1px solid var(--line); border-radius:var(--r-in);
            background:var(--surface-2); padding:17px 16px; }
  /* The peek leads. Seven real pages readable before paying is the strongest
     thing this page can say and it was a text link under a price. */
  /* THE BAND IS THE WHOLE TRICK. Both readings are in the markup and one is
     hidden, so the block is the same height whether or not there is an offer
     and the button under it never moves. min-height rather than a fixed one
     so a two-line saving in a long currency still fits. */
  .rp-band { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
             min-height:26px; margin:0 0 12px; }
  /* EXCEPT WHEN THERE IS NOTHING IN IT AT ALL. On a single report both
     readings are always in the markup and one is hidden, so this band is
     never empty and never collapses — which is the reflow guard above, and
     what checks/reportbuy.mjs measures. A BUNDLE has no SKU: its struck
     total is arithmetic over the parts, so REPORT_OFFER_JS removes both
     halves outright in any currency but rupees rather than converting a
     price we do not offer. That left 38px of nothing above "Get all five"
     for every reader outside India. :empty tells the two cases apart on the
     only thing that actually differs between them — hidden children are
     still children. */
  .rp-band:empty { display:none; margin:0; min-height:0; }
  /* AND WHEN THE ANSWER IS IN AND IT IS "no offer". The two halves ship
     hidden and, for a reader whose currency carries no offer on this
     report, stay hidden forever — so the reserve above was a permanent
     38px of nothing at the top of the panel for everyone outside India.
     `:empty` cannot see it: hidden children are still children, which is
     the distinction the rule above was written for.
     .rp-band-done is set by REPORT_OFFER_JS at both of its settle points,
     the pre-paint replay and the fetch, so the band holds its height
     while the answer is unknown and collapses only once it is known to be
     nothing. Without the class this rule would fire on first paint and
     the offer landing 400ms later would push the button down — the flash
     the reserve exists to prevent, mirrored. */
  .rp-band-done:not(:has(> :not([hidden]))) {
    display:none; margin:0; min-height:0; }

  /* ── AND ONCE IT IS PAID FOR, THE SHOP FURNITURE GOES ──────────────
     The band above reserves 26px + 12 so the button cannot move when an
     offer starts or ends. That is right up to the moment the money has
     moved, after which there is no offer to start or end and the reserve
     is 38px of nothing at the top of the card — which is what it looked
     like on a phone, an empty third of the panel above the first word.

     The mail line goes for a stronger reason than space: "We'll email it
     to <address>" is a promise in the future tense, and the status line
     under the button has by then said whether the mail actually went. On
     an install with no mail provider the two sat one above the other
     saying opposite things. Change does nothing at this point either —
     the address was read at purchase and the report has already been
     written to it or not.

     The reassurances are the same case. "Ready in seconds" and "One
     payment, not a subscription" are answers to "should I buy this",
     printed under a report the reader has bought. */
  .rp-pay-got .rp-band,
  .rp-pay-got .rp-buyer,
  .rp-pay-got .rp-mail,
  .rp-pay-got .rp-sure { display:none; }
  .rp-mrp { font-family:var(--font-mono); font-size:13.5px; color:var(--ink-3);
            text-decoration:line-through; text-decoration-thickness:1px; }
  .rp-off { font-family:var(--font-mono); font-size:9.5px; letter-spacing:.07em;
            text-transform:uppercase; font-weight:700; padding:3px 8px;
            border-radius:var(--r-pill); font-style:normal;
            color:var(--good); background:var(--good-bg); }
  /* THE REASSURANCES USED TO LIVE IN THE BAND AND NEVER RENDERED.
     Two spans, "One-time payment" and "No subscription", hidden by
     `.rp-band:has(.rp-off:not([hidden])) .rp-plain` — and every report
     carries an offer, so the rule fired on all twelve pages, every time.
     Measured at three widths: nothing. The page had no reassurance at all
     while appearing to have two lines of it.
     They are below the button now, where they do not compete with the price
     for a row. The band keeps its height on its own so an offer starting or
     ending still moves nothing — the property checks/reportbuy.mjs measures,
     which the two spans used to provide by accident. */
  .rp-band { min-height:26px; }

  /* ── whose kundli this is worked out from ──────────────────────────
     Collapsed it is a statement; pressed it becomes the list and closes
     again on choosing. It sits above the address because the two answer
     the same shape of question in the order they matter: who it is about,
     then where it goes. */
  /* display:block IS DELIBERATE, not a default. The first version of this
     set no display and inherited grid from .rp-who — a class that already
     existed on the bundle page — which sized its track to the row's
     min-content and pushed the row 39px past the panel's right border. The
     class was renamed; the explicit display stays so the next collision
     cannot do it again. */
  .rp-buyer { display:block; margin:0 0 13px; }
  /* IT HOLDS ITS PLACE WHILE IT IS UNKNOWN, exactly like .rp-band above and
     for the same reason. profiles/ is a round trip, so shipping this
     display:none and revealing it on the answer pushed the buy button down
     72px about half a second after the page settled — the reader's own
     measurement of the panel, taken while they were reaching for it.
     visibility rather than display: the box is laid out at its real height
     and paints nothing, and an invisible row is out of the tab order and
     out of the accessibility tree, so nothing here is reachable before it
     says anything. */
  .rp-buyer[hidden] { visibility:hidden; }
  /* ── THE WAIT, DRAWN ───────────────────────────────────────────────
     Two round trips before this row can say anything, and it used to spend
     them invisible. Same gradient and the same c-px-sh keyframes a price
     shimmers with, because it is the same promise: something specific is
     coming and the space it will take is already yours.

     THE BARS ARE SIZED, because the elements they cover are empty until the
     answer lands — a skeleton drawn on a zero-width box is no skeleton. The
     heights match the type they stand in for so the row does not resize when
     the real name arrives.

     NOT PRESSABLE WHILE IT WAITS. The button opens a list built from
     App.profiles(), which is empty until the same request that fills this
     answers, so a press here opened an empty box. */
  .rp-buyer-wait .rp-buyer-b { pointer-events:none; }
  .rp-buyer-wait .rp-buyer-x { visibility:hidden; }
  .rp-buyer-wait .rp-buyer-av,
  .rp-buyer-wait .rp-buyer-n,
  .rp-buyer-wait .rp-buyer-d {
      color:transparent; border-radius:5px;
      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; }
  .rp-buyer-wait .rp-buyer-n { display:block; width:62%; height:13px;
                               margin-bottom:5px; }
  .rp-buyer-wait .rp-buyer-d { display:block; width:88%; height:11px; }
  @media (prefers-reduced-motion:reduce) {
    .rp-buyer-wait .rp-buyer-av,
    .rp-buyer-wait .rp-buyer-n,
    .rp-buyer-wait .rp-buyer-d { animation:none; }
  }
  /* AND IT GOES ENTIRELY when the account has no kundli at all. Then there
     is no row coming and the reserve would be the band's empty third again,
     on a panel already saying "Add a kundli". */
  .rp-buyer-off, .rp-buyer-off[hidden] { display:none; }
  .rp-buyer-h { margin:0 0 8px; font:600 10px/1 var(--font-mono);
              letter-spacing:.13em; text-transform:uppercase; color:var(--ink-3); }
  .rp-buyer-s + .rp-buyer-s { margin-top:8px; }
  /* min-width:0 on both, or a grid or flex ancestor sizes them to the row's
     min-content — which includes an unshrinkable avatar, the rank and the
     Change chip — and the panel scrolls sideways. */
  .rp-buyer-s { min-width:0; }
  .rp-buyer-b { display:flex; align-items:center; gap:11px; width:100%;
              min-width:0;
              min-height:56px; padding:9px 11px; font:inherit; text-align:left;
              border:1px solid var(--line); border-radius:10px;
              background:var(--cream); color:var(--ink); cursor:pointer; }
  .rp-buyer-b:hover { border-color:var(--gold); }
  .rp-buyer-k { flex:none; width:20px; font:700 9.5px/1 var(--font-mono);
              letter-spacing:.1em; color:var(--ink-3); }
  .rp-buyer-av { flex:none; width:34px; height:34px; border-radius:50%;
               display:grid; place-items:center; background:var(--gold-bg);
               color:var(--gold-dk); font-weight:700; font-size:14px; }
  /* min-width:0 or the birth line refuses to ellipsise: a flex item's
     automatic minimum size is its content, so the row grows instead and
     takes the panel sideways with it. */
  .rp-buyer-t { flex:1; min-width:0; }
  .rp-buyer-n { display:block; font-weight:700; font-size:14px;
              letter-spacing:-.01em; }
  /* IT WRAPS, IT DOES NOT ELLIPSISE. This was one nowrap line with a
     text-overflow, and the panel is 244px in the desktop sidebar and 250 on
     a 320px handset — so at both ends it rendered "11 Mar 1994 · 07:2…",
     cutting the birth TIME in half. The time is the reason this line is
     here: two kundlis on one account often share a surname and a city and
     differ by the minute that decides the lagna. A second line costs 17px;
     a truncated one costs the whole feature. */
  .rp-buyer-d { display:block; font-style:normal; font-size:11.5px;
              line-height:1.45; color:var(--ink-3); }
  .rp-buyer-x { flex:none; display:flex; align-items:center; gap:3px;
              font-size:12.5px; font-weight:600; color:var(--gold-dk); }
  .rp-buyer-x i { font-style:normal; }
  /* THE VERB GOES BEFORE THE BIRTH LINE DOES. The row is rank + avatar +
     name + "Change" + chevron, and the first thing to give was the one
     field that tells two kundlis apart: measured "11 Mar 1994 · 07:2…".
     The chevron and the dashed edge already say the row opens, and the
     button carries an aria-label with the verb in it.

     A CONTAINER QUERY, NOT A VIEWPORT ONE. The first version hid the word
     below 420px and still clipped at 1440 — because the panel is a 244px
     sidebar column on a desktop and a 288px full-width block on a 390px
     phone, so the widest viewport has the NARROWEST panel and a media
     query gets the relationship backwards. What decides whether the word
     fits is this box, so this box is what is asked. */
  .rp-pay { container-type:inline-size; }
  @container (max-width:340px) { .rp-buyer-x i { display:none; } }
  .rp-buyer-x svg { transition:transform .16s; }
  .rp-buyer-b[aria-expanded="true"] .rp-buyer-x svg { transform:rotate(180deg); }
  /* THE ONE THAT IS NOT CHOSEN YET says so in the slot rather than beside
     it — a dashed edge and the verb, so an empty second kundli reads as
     something to do, not something that failed to load. */
  .rp-buyer-s-none .rp-buyer-b { border-style:dashed; border-color:var(--gold); }
  .rp-buyer-s-none .rp-buyer-n { color:var(--gold-dk); font-weight:600; }
  .rp-buyer-s-none .rp-buyer-av { color:var(--gold-dk); }

  .rp-buyer-l { border:1px solid var(--gold); border-radius:10px;
              background:var(--cream); overflow:hidden; margin-top:6px; }
  .rp-buyer-l[hidden] { display:none; }
  .rp-buyer-i { display:flex; align-items:center; gap:10px; width:100%;
              padding:10px 12px; font:inherit; text-align:left; cursor:pointer;
              background:none; color:var(--ink); border:0;
              border-top:1px solid var(--line); }
  .rp-buyer-i:first-child { border-top:0; }
  .rp-buyer-i:hover { background:var(--gold-bg); }
  .rp-buyer-r { flex:none; width:17px; height:17px; border-radius:50%;
              border:1.5px solid var(--ink-3); display:grid; place-items:center; }
  .rp-buyer-i-on .rp-buyer-r { border-color:var(--gold-solid); }
  .rp-buyer-i-on .rp-buyer-r::after { content:''; width:9px; height:9px;
              border-radius:50%; background:var(--gold-solid); }
  .rp-buyer-i .rp-buyer-n { font-weight:600; font-size:13.5px; }
  .rp-buyer-add { color:var(--gold-dk); font-weight:600; font-size:13px;
                text-decoration:none; }
  .rp-buyer-add .rp-buyer-r { border:0; }
  /* A SLOT CANNOT OFFER THE PERSON IN THE OTHER SLOT. Matching one kundli
     against itself is not a reading, and the endpoint would take the money
     for it. Hidden rather than absent so the list does not reflow as the
     other slot changes. */
  .rp-buyer-i-used { display:none; }
  .rp-sure { list-style:none; margin:10px 0 0; padding:0; display:grid;
             gap:5px; font-size:12px; line-height:1.5; color:var(--ink-3); }
  .rp-sure li { position:relative; padding-left:13px; }
  .rp-sure li::before { content:''; position:absolute; left:0; top:7px;
                        width:4px; height:4px; border-radius:50%;
                        background:var(--gold); opacity:.65; }
  .rp-sure a { color:var(--gold-dk); }
  /* THE PRICE RIDES THE BUTTON, which is what the app does. One thing to
     press and the number is on it — it was in a meta row beside Sections and
     Format, as though what it costs were a fact about the report. */
  .rp-payb { display:flex; align-items:center; justify-content:space-between;
              gap:10px; width:100%; min-height:48px; padding:0 16px;
              border-radius:10px; background:var(--gold-solid);
              color:var(--gold-solid-text); font-weight:700; font-size:14.5px;
              text-decoration:none; border:1px solid var(--gold-solid); }
  .rp-payb:hover { filter:brightness(1.06); }
  .rp-payb b { font-family:var(--font-mono); font-size:19px; font-weight:700;
                letter-spacing:-.01em; font-variant-numeric:tabular-nums;
                line-height:1.2; }
  /* WHAT THE WALLET SAYS, and only on the signed-in copy. Under the button
     rather than over it, so nothing this fills in can move the control the
     reader is already aiming at. */
  .rp-wal { margin:9px 0 0; font-size:12.5px; line-height:1.55;
            color:var(--ink-3); }
  .rp-wal b { font-weight:600; color:var(--ink-2);
              font-variant-numeric:tabular-nums; }
  .rp-wal a { color:var(--gold-dk); }
  .rp-lede { font-size:15px; line-height:1.62; color:var(--ink-2);
             margin:0 0 20px; max-width:58ch; }
  /* .rp-meta, .rp-buy AND .rp-fine WENT WITH THE BUNDLE HERO they were
     written for. The Reports / Sections / Format row is two lines of "What
     you get" now, the button is .rp-payb like every other one on the site,
     and the fine print is the first of the three reassurances under it. */
  /* ABOVE THE RULE, with the facts, because looking is what a reader does
     before deciding — under it, beside "Get this report", it was competing
     with the buy rather than leading to it. */
  .rp-see { display:inline-flex; align-items:center; gap:8px;
            align-self:flex-start; margin:6px 0 18px; padding:9px 15px;
            font-size:13.5px; font-weight:600; border-radius:var(--r-in);
            background:var(--surface-2); color:var(--ink);
            box-shadow:inset 0 0 0 1px var(--line); text-decoration:none;
            transition:box-shadow .16s, background .16s; }
  .rp-see:hover { background:var(--gold-bg);
                  box-shadow:inset 0 0 0 1px var(--gold); }
  .rp-see svg { color:var(--gold-dk); }

  /* ── the reader ── */
  .rp-pv { position:fixed; inset:0; z-index:90; display:grid;
           grid-template-rows:auto minmax(0,1fr); background:var(--cream); }
  .rp-pv[hidden] { display:none; }
  .rp-pv-bar { display:flex; align-items:center; gap:14px; flex-wrap:wrap;
               padding:10px 16px; border-bottom:1px solid var(--line);
               background:var(--surface); }
  .rp-pv-t { flex:1 1 200px; min-width:0; font-family:var(--font-serif);
             font-size:15px; font-weight:600; overflow:hidden;
             text-overflow:ellipsis; white-space:nowrap; }
  .rp-pv-c { font-family:var(--font-mono); font-size:10px; letter-spacing:.1em;
             text-transform:uppercase; color:var(--ink-3); }
  .rp-pv-dl { font-size:13px; color:var(--gold-dk); }
  .rp-pv-buy { display:inline-flex; align-items:center; gap:14px;
               padding:9px 16px; border-radius:var(--r-in); font-size:13px;
               font-weight:600; background:var(--gold-solid);
               color:var(--gold-solid-text); text-decoration:none; }
  .rp-pv-buy b { font-weight:700; font-variant-numeric:tabular-nums; }
  .rp-pv-x { font:inherit; cursor:pointer; font-size:13px; font-weight:600;
             padding:9px 15px; border-radius:var(--r-pill);
             border:1px solid var(--line); background:var(--surface-2);
             color:var(--ink); }
  .rp-pv-s { overflow:auto; overscroll-behavior:contain;
             display:grid; justify-items:center; gap:20px;
             padding:22px 18px 40px; background:var(--surface-2); }
  .rp-pv-p { position:relative; margin:0; width:min(840px,100%);
             scroll-margin-top:18px; }
  .rp-pv-i { display:block; width:100%; height:auto; aspect-ratio:1 / 1.414;
             object-fit:cover; object-position:top; border-radius:8px;
             background:#fff; box-shadow:0 10px 34px rgba(0,0,0,.34); }
  .rp-pv-p figcaption { position:absolute; right:10px; bottom:10px;
                        min-width:24px; padding:3px 8px; text-align:center;
                        border-radius:var(--r-pill);
                        font-family:var(--font-mono); font-size:11px;
                        background:var(--gold-solid);
                        color:var(--gold-solid-text); }
  /* The pages past the sample: real, at a fourteenth of the area, blurred.
     The count and the shape are the honest part; the words are not on offer. */
  .rp-pv-b .rp-pv-i { filter:blur(6px); opacity:.6; }
  .rp-pv-b figcaption { background:var(--surface); color:var(--ink-3);
                        border:1px solid var(--line); }
  .rp-pv-cut { width:min(840px,100%); margin:6px 0; padding:13px 17px;
               border-radius:12px; border:1px dashed var(--line);
               background:var(--surface); font-size:13.5px;
               color:var(--ink-2); text-align:center; }
  @media (prefers-reduced-motion:reduce) { .rp-see { transition:none; } }
  /* The top bar is sticky, so an anchor lands under it without this. */
  #rp-look { display:block; scroll-margin-top:86px; }

  /* ── a look inside ── */
  /* A4 at 1:1.414. The box is reserved by aspect-ratio rather than by the
     image's own dimensions, so six pages of slightly different renders line
     up on one baseline instead of stepping. */
  /* A SHELF UNTIL THERE IS ROOM FOR ONE ROW.
     This was a 2 / 3 / 7 column grid, and seven pages do not divide by two
     or by three: measured rows of [2,2,2,1] at 390px and [3,3,1] at 768px,
     with the seventh alone in the last row reading as a page that failed to
     load rather than the last one. It was also 1,011px tall on a phone —
     sixteen per cent of the document — for a block whose job is a glance.
     The bundle variant orphans the same way, three members in two columns.
     A row that runs off the edge cannot orphan anything at any count, costs
     one row's height, and is the shape the blurred remainder underneath it
     already uses.
     Fixed tile widths rather than fractions, because a flex row of
     percentages sized to the SCROLLER, not the screen, andseven tiles each
     1/7 of an off-screen width is not a thumbnail. */
  .rp-peek { display:flex; gap:13px; margin:0 0 4px; overflow-x:auto;
             padding-bottom:6px; scroll-snap-type:x proximity;
             scrollbar-width:thin; overscroll-behavior-x:contain; }
  .rp-peek > .rp-pk { flex:0 0 auto; width:132px; scroll-snap-align:start; }
  @media (min-width:640px) { .rp-peek > .rp-pk { width:150px; } }
  /* Seven across is the whole point of the wide layout: the report is that
     many pages and you can see all of them at once. */
  @media (min-width:1020px) {
    .rp-peek { display:grid; grid-template-columns:repeat(7,1fr);
               overflow:visible; padding-bottom:0; }
    .rp-peek > .rp-pk { width:auto; }
    /* .rp-peek-b IS GONE. It sized a row of member covers back when a
       bundle's "look inside" was one page from each report. A bundle now
       shows the SAME strip a single report does — seven pages of whichever
       report is picked — so it wants the same seven columns, not a variant. */
  }
  .rp-pk { position:relative; display:block; border-radius:9px;
           overflow:hidden; border:1px solid var(--line);
           background:var(--surface); text-decoration:none;
           transition:border-color .16s, transform .16s; }
  .rp-pk:hover { border-color:var(--gold); transform:translateY(-2px); }
  /* ONE RING, EVERY CONTROL ON THE PAGE. This existed for .rp-pk alone,
     so the sample thumbnails had a gold outline and the button that takes
     the money did not — .rp-payb, "See sample report", the back link, the
     download on the shelf and every control in the reader fell back to
     whatever the browser draws, which on a dark ground is a thin ring that
     is nearly invisible. A keyboard reader tabbing to the purchase button
     could not tell they had arrived.
     Listed rather than applied to :any-link, because a blanket rule would
     also ring the twelve links inside the section list and the breadcrumb,
     which already carry their own. */
  .rp-pk:focus-visible,
  .rp-payb:focus-visible,
  .rp-see:focus-visible,
  .rp-backb:focus-visible,
  .rp-act:focus-visible,
  .rp-pv-dl:focus-visible,
  .rp-pv-buy:focus-visible,
  .rp-pv-x:focus-visible,
  .rp-sure a:focus-visible,
  .rp-rest a:focus-visible { outline:2px solid var(--gold);
                             outline-offset:2px; border-radius:6px; }
  .rp-pk-i { display:block; width:100%; height:auto; aspect-ratio:1 / 1.414;
             object-fit:cover; object-position:top; }
  .rp-pk-n { position:absolute; right:6px; bottom:6px; min-width:20px;
             padding:2px 6px; border-radius:var(--r-pill); text-align:center;
             font-family:var(--font-mono); font-size:10px;
             background:var(--gold-solid); color:var(--gold-solid-text); }
  .rp-pk-t { display:block; padding:8px 10px; font-size:12px;
             line-height:1.4; color:var(--ink-2);
             border-top:1px solid var(--line-soft); }
  /* The rest of the document, at a fourteenth of the area and blurred on the
     container rather than per tile — 72 filters is 72 layers to composite. */
  .rp-rest { margin-top:12px; }
  /* A SHELF EDGE, NOT A WALL.
     This drew every remaining page as a grid — 72 of them on Janam Kundli,
     1,486px on a 390px screen, twenty per cent of the page, and the single
     largest element on a page whose job is to sell a report. It carried one
     fact, and the sentence above it already said that fact.
     In light it was grey mush. In DARK it was worse than useless: the sample
     pages are white paper, so blurred at half opacity on a dark ground they
     became a wall of glowing tiles directly under the gold buy button — the
     brightest thing on the screen, pulling the eye straight off the one
     control that matters. The bottom mask barely touched it fourteen rows
     down.
     One row now, running off the right edge under a fade, so it reads as a
     document continuing past the frame rather than as a field of tiles. */
  .rp-rest-g { display:flex; gap:8px; overflow:hidden;
               filter:blur(2px); opacity:.45;
               -webkit-mask-image:linear-gradient(to right,#000 0 42%,
                                                  transparent 100%);
               mask-image:linear-gradient(to right,#000 0 42%,
                                          transparent 100%); }
  /* flex-shrink:0 or the row squeezes fourteen pages into the width instead
     of overflowing it, and there is nothing for the fade to cut. */
  .rp-rest-i { flex:0 0 auto; width:96px; height:auto; aspect-ratio:1 / 1.414;
               object-fit:cover; object-position:top; border-radius:4px;
               background:var(--surface); }
  @media (max-width:600px) { .rp-rest-i { width:74px; } }
  /* THE COUNT IS A CAPTION NOW, NOT A PILL FLOATING IN THE MIDDLE.
     A raised pill on a shadow was the right answer when it had to sit on top
     of fourteen rows of blurred tiles and stay readable. Over one strip there
     is nothing to sit on top of, and a badge hovering over a 130px row reads
     as a control. It goes above the strip and reads as a sentence, which is
     what it is. */
  .rp-rest-say { display:flex; align-items:baseline; gap:9px; flex-wrap:wrap;
                 margin:0 0 9px; }
  .rp-rest-say b { font-family:var(--font-serif); font-size:17px;
                   font-weight:600; color:var(--ink); }
  .rp-rest-say i { font-style:normal; font-size:12.5px; color:var(--ink-3); }
  /* :target, so a page enlarges with no script at all. */
  .rp-lb { display:none; }
  .rp-lb:target { position:fixed; inset:0; z-index:80; display:grid;
                  place-items:center; padding:22px;
                  background:rgba(0,0,0,.72); overflow:auto; }
  .rp-lb-bg { position:absolute; inset:0; }
  .rp-lb-i { position:relative; display:block; max-width:min(860px,100%);
             width:100%; height:auto; border-radius:10px;
             box-shadow:0 18px 60px rgba(0,0,0,.5); }
  /* Fixed, not absolute: the overlay scrolls when a page is taller than the
     window, and an absolute Close scrolls off with it. */
  .rp-lb-x { position:fixed; top:14px; right:16px; z-index:1;
             padding:8px 15px; border-radius:var(--r-pill); font-size:13px;
             font-weight:600; background:var(--gold-solid);
             color:var(--gold-solid-text); text-decoration:none; }
  @media (prefers-reduced-motion:reduce) {
    .rp-pk { transition:none; }
    .rp-pk:hover { transform:none; }
  }

  .rp-secs { list-style:none; margin:0 0 4px; padding:0;
             display:grid; gap:1px; background:var(--line-soft);
             border:1px solid var(--line-soft); border-radius:12px;
             overflow:hidden; }
  @media (min-width:760px) { .rp-secs { grid-template-columns:1fr 1fr; } }
  /* THE CELL THAT ISN'T THERE. The grid draws its hairlines as `gap:1px` over
     a --line-soft background, so a row with one item shows the background
     through the empty half — a filled block that reads as a rendering fault
     rather than as the end of a list. It was there when the lists were nine
     long and unmissable the moment they became thirty-three.
     An odd last item takes the whole row instead. */
  .rp-secs li:last-child:nth-child(odd) { grid-column:1 / -1; }
  .rp-secs li { display:flex; gap:12px; align-items:baseline;
                padding:13px 16px; background:var(--surface);
                font-size:13.5px; color:var(--ink); }
  .rp-secs li span { font-family:var(--font-mono); font-size:10.5px;
                     color:var(--gold-dk); }
  .rp-highs { margin:0; padding:0; list-style:none; display:grid; gap:9px; }
  .rp-highs li { position:relative; padding-left:20px; font-size:13.5px;
                 line-height:1.6; color:var(--ink-2); max-width:74ch; }
  .rp-highs li::before { content:''; position:absolute; left:0; top:8px;
                         width:7px; height:7px; border-radius:50%;
                         background:var(--gold); }
  .rp-who { display:grid; gap:13px; grid-template-columns:1fr; }
  @media (min-width:760px) { .rp-who { grid-template-columns:1fr 1fr; } }
  @media (min-width:1180px) { .rp-who { grid-template-columns:repeat(3,1fr); } }
  .rp-buyer-c b { display:block; font-family:var(--font-serif); font-size:16px;
                font-weight:600; margin-bottom:5px; }
  .rp-buyer-c p { margin:0; font-size:13px; line-height:1.6;
                color:var(--ink-2); }
  /* THE WAY BACK IS A CONTROL, SO IT LOOKS LIKE ONE. It was "&larr; All
     reports" — a line of 13.5px text alone at the foot of a long page, which
     is the least visible thing on it and the one every reader who did not buy
     needs. Same shape as "See sample report" above it, so the page has one
     kind of secondary button rather than two. The arrow goes: it is not a
     step backwards through a flow, it is the shelf. */
  .rp-back { margin:30px 0 0; }
  .rp-backb { display:inline-flex; align-items:center; gap:8px;
              min-height:44px; padding:0 20px; font-size:14px; font-weight:600;
              border-radius:var(--r-in); background:var(--surface-2);
              color:var(--ink); box-shadow:inset 0 0 0 1px var(--line);
              text-decoration:none; transition:box-shadow .16s, background .16s; }
  .rp-backb::before { content:''; width:7px; height:7px; border-radius:50%;
                      background:var(--gold); flex:none; }
  .rp-backb:hover { background:var(--gold-bg);
                    box-shadow:inset 0 0 0 1px var(--gold); }
  @media (prefers-reduced-motion:reduce) { .rp-backb { transition:none; } }

  /* ── a bundle ── */
  /* ── THE HERO ART IS A CAROUSEL, AND IT DRIVES THE PAGE ────────────
     One cover at a time, in place, with a dot per report — and the dots and
     the cards down in "A look inside" are the same choice, not two.

     THAT IS WHY THE RADIOS ARE THE FIRST THING IN <main>. A sibling
     combinator only ever looks forward, so an input can style anything after
     it and nothing before it. They used to sit inside .rp-sel, which put the
     hero out of reach; from the top of the document they reach the carousel,
     the dots, the cards and the panes, and one press moves all four. The
     inputs are the only <input> children main has, which is what makes
     nth-of-type a stable index into the bundle's members.

     WHAT WAS HERE BEFORE. A deck — the covers overlapped into a stack. It
     survived two earlier attempts that this file records because each failed
     for a reason worth keeping: a small centred row put 146px of art in a
     490px column, which is the empty panel this whole section was reported
     as; and height:100% with object-fit:cover filled the column by cropping
     the front cover to a quarter of its width, cutting the title off at both
     ends. A carousel has neither problem — every cover is shown whole, at
     the same size, and the column is as tall as one of them. */
  /* FLUSH TO THREE EDGES, like the single report's cover. It was inset —
     an 8px-radius card floating on 14px of panel colour inside another
     card's border, which is a picture in a frame in a frame, and it made the
     hero taller than the cover it was showing. Top, left and right belong to
     the art; the bottom is the dots' strip, because they are controls and a
     control on top of a dark painting needs a scrim to be legible in both
     themes, which is more machinery than a 34px band. */
  /* AND NO PANEL COLOUR UNDER IT. .rp-hero .rp-art paints surface-2, which
     is invisible on a single report because the cover fills the column. Here
     the column is only as tall as one cover and the case beside it can be
     taller — the wallet line under the buy button is the one that does it —
     so the leftover read as a second empty box tacked to the bottom of the
     art. Transparent, it is the card, and the cover simply sits at the top
     of it. */
  .rp-hero .rp-art-b { display:flex; flex-direction:column;
                       justify-content:flex-start; padding:0;
                       background:none; overflow:hidden; }
  .rp-cov-w { overflow:hidden; width:100%; }
  .rp-cov-t { display:flex; width:100%;
              transition:transform .38s cubic-bezier(.4,0,.2,1); }
  .rp-cov-t img { flex:0 0 100%; display:block; width:100%; height:auto; }
  /* Under 640 the hero is one column, so the art is as wide as the card —
     and a cover at 358px wide is 506 tall, half a phone screen of picture
     before a word of the offer. 240 puts it at 339, which is where
     .rp-hero .rp-cover caps itself on the same screens. */
  /* THE CAROUSEL FILLS IT TOO, and the same crop for the same reason. Edge
     to edge is also what makes a carousel legible as one: a strip that runs
     to both borders reads as something that slides, where a centred card
     with margins reads as a picture that happens to change. */
  @media (max-width:639px) {
    .rp-hero .rp-art-b { padding:0; }
    .rp-cov-w { max-width:none; margin:0; border-radius:0; }
    .rp-cov-t img { height:auto; border-radius:0; }
    .rp-dots { padding:12px 0 14px; }
  }
  @media (prefers-reduced-motion:reduce) { .rp-cov-t { transition:none; } }
  .rp-r:nth-of-type(1):checked ~ .rp-top .rp-cov-t { transform:translateX(-0%); }
  .rp-r:nth-of-type(2):checked ~ .rp-top .rp-cov-t { transform:translateX(-100%); }
  .rp-r:nth-of-type(3):checked ~ .rp-top .rp-cov-t { transform:translateX(-200%); }
  .rp-r:nth-of-type(4):checked ~ .rp-top .rp-cov-t { transform:translateX(-300%); }
  .rp-r:nth-of-type(5):checked ~ .rp-top .rp-cov-t { transform:translateX(-400%); }
  /* The dots are labels for the same radios, so they are the control and not
     a read-out of one. 22px square with a 9px mark inside it: the mark is
     what the eye follows and the square is what a finger has to hit. */
  .rp-dots { display:flex; gap:2px; justify-content:center;
             padding:8px 0 10px; }
  .rp-dot { display:grid; place-items:center; width:22px; height:22px;
            cursor:pointer; }
  .rp-dot::before { content:''; width:8px; height:8px; border-radius:50%;
                    background:var(--line); transition:background .16s,
                    transform .16s; }
  .rp-dot:hover::before { background:var(--gold); }
  @media (prefers-reduced-motion:reduce) { .rp-dot::before { transition:none; } }
  .rp-r:nth-of-type(1):checked ~ .rp-top .rp-dot:nth-child(1)::before,
  .rp-r:nth-of-type(2):checked ~ .rp-top .rp-dot:nth-child(2)::before,
  .rp-r:nth-of-type(3):checked ~ .rp-top .rp-dot:nth-child(3)::before,
  .rp-r:nth-of-type(4):checked ~ .rp-top .rp-dot:nth-child(4)::before,
  .rp-r:nth-of-type(5):checked ~ .rp-top .rp-dot:nth-child(5)::before {
    background:var(--gold-dk); transform:scale(1.3); }

  /* The selector below. The cards and the panes are paired by POSITION —
     one nth-of-type rule per member, generated to the length of the longest
     bundle, instead of one rule per report per bundle. */
  .rp-sel { display:grid; gap:16px; min-width:0; }
  /* fixed, NOT absolute — AND THAT IS NOT A STYLING DETAIL. Pressing a
     label activates the input it names and moves focus to it, and a browser
     scrolls a newly focused element into view. Absolutely positioned, these
     sit at their static place, which is the top of <main>; so choosing a
     report from the picker three screens down threw the reader back to the
     title. Pinned to the viewport they are always already in view, so there
     is nothing to scroll to, and the reader stays where the pages are.
     A combinator is a DOM relationship, not a layout one, so the four
     :checked ladders below are untouched by this. */
  .rp-r { position:fixed; top:0; left:0; width:1px; height:1px; margin:0;
          padding:0; overflow:hidden; clip:rect(0 0 0 0);
          white-space:nowrap; border:0; }
  /* .rp-sel-cards AND .rp-sel-c ARE GONE with the second picker. The dots
     in the hero are the control, and they were always the same radios these
     labels pointed at — see the ladders below, which now drive the pane
     alone. */
  /* auto-fit with a ceiling: the same tile at three members as at five,
     and no row can orphan one. */
  .rp-sel-cards { display:grid; gap:10px; min-width:0; justify-content:start;
                  grid-template-columns:repeat(auto-fit,minmax(136px,204px));
                  margin:0 0 18px; }
  /* THE BORDER DOES NOT TRANSITION, AND THAT IS A BUG FIX. It used to,
     and with script off on a cold cache the stylesheet lands after the
     first style resolution — so the ladder's gold edge arrived as a style
     CHANGE and faded in over 150ms on every load nobody asked for an
     animation on. Caught by a check reading borderColor at t=0 and getting
     `currentColor` seven loads in eight; at 60ms it was gone, eight in
     eight, which is what named it. The background never flickered because
     it was not in this list.
     box-shadow and transform stay: those fire on hover, which is a real
     interaction and where the movement belongs. Selection itself snaps,
     and it should — the carousel already slides. */
  .rp-sel-c { display:grid; gap:6px; justify-items:center; cursor:pointer;
              padding:10px 8px 11px; border-radius:12px; text-align:center;
              background:var(--surface); border:1.5px solid var(--line);
              transition:box-shadow .15s, transform .15s; }
  .rp-sel-c:hover { border-color:var(--gold); transform:translateY(-2px); }
  @media (prefers-reduced-motion:reduce) { .rp-sel-c { transition:none; } }
  /* height:auto, NOT aspect-ratio. The covers carry width/height attributes
     (520x735), so constraining only the width left the element 735px tall
     and the five cards filled the viewport. */
  .rp-sel-i { display:block; width:100%; max-width:128px; height:auto;
              border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,.16); }
  .rp-sel-t { font-size:11.5px; line-height:1.3; font-weight:600;
              color:var(--ink); }
  .rp-sel-c em { font-style:normal; font-size:11px; color:var(--ink-3);
                 font-variant-numeric:tabular-nums; }
  /* Only the pane of the checked card is in the flow. */
  .rp-sel-p { display:none; }
  .rp-sel-pane { min-width:0; }
  /* NO PANEL AROUND IT. The pane holds exactly what a single report's
     "A look inside" holds — the seven-page strip and the blurred remainder
     — and on that page they sit on the document, not in a box. A border
     here would be the one difference between the two, on the one thing the
     reader is comparing. The selected card's gold ring is what joins them. */
  .rp-sel-say { margin:0 0 12px; font-size:13.5px; line-height:1.6;
                color:var(--ink-2); max-width:64ch; }
  .rp-sel-say b { color:var(--ink); font-weight:600; }
  .rp-sel-say a { color:var(--gold-dk); font-weight:600;
                  white-space:nowrap; }
  .rp-r:nth-of-type(1):checked ~ .rp-sel .rp-sel-c:nth-child(1),
  .rp-r:nth-of-type(2):checked ~ .rp-sel .rp-sel-c:nth-child(2),
  .rp-r:nth-of-type(3):checked ~ .rp-sel .rp-sel-c:nth-child(3),
  .rp-r:nth-of-type(4):checked ~ .rp-sel .rp-sel-c:nth-child(4),
  .rp-r:nth-of-type(5):checked ~ .rp-sel .rp-sel-c:nth-child(5) {
    border-color:var(--gold); background:var(--gold-bg);
    box-shadow:0 3px 14px rgba(0,0,0,.10); }
  .rp-r:nth-of-type(1):focus-visible ~ .rp-sel .rp-sel-c:nth-child(1),
  .rp-r:nth-of-type(2):focus-visible ~ .rp-sel .rp-sel-c:nth-child(2),
  .rp-r:nth-of-type(3):focus-visible ~ .rp-sel .rp-sel-c:nth-child(3),
  .rp-r:nth-of-type(4):focus-visible ~ .rp-sel .rp-sel-c:nth-child(4),
  .rp-r:nth-of-type(5):focus-visible ~ .rp-sel .rp-sel-c:nth-child(5) {
    outline:2px solid var(--gold-dk); outline-offset:2px; }
  .rp-r:nth-of-type(1):checked ~ .rp-sel .rp-sel-p:nth-child(1),
  .rp-r:nth-of-type(2):checked ~ .rp-sel .rp-sel-p:nth-child(2),
  .rp-r:nth-of-type(3):checked ~ .rp-sel .rp-sel-p:nth-child(3),
  .rp-r:nth-of-type(4):checked ~ .rp-sel .rp-sel-p:nth-child(4),
  .rp-r:nth-of-type(5):checked ~ .rp-sel .rp-sel-p:nth-child(5) {
    display:block; }

  /* KEYBOARD, AND THE RING HAS TO BE WHERE THE READER IS LOOKING. The radio
     itself is clipped to a pixel, so the indication is drawn on its labels —
     and there are two of them, in different parts of the page. Only the card
     used to get it, three screens below the fold: tabbing into the page put
     focus on an invisible control and drew the ring somewhere the reader
     could not see. Both now, so whichever of the two is on screen shows it.

     WHAT IS STILL MISSING, said plainly: the group has no name. The radios
     are direct children of <main> because every ladder above reaches forward
     from them with ~, and wrapping them in a labelled role="radiogroup"
     would put an element between them and everything they style. Each radio
     is named by its card's text; the group is not announced as one. */
  .rp-r:focus-visible ~ .rp-top .rp-dot { outline:none; }
  .rp-r:nth-of-type(1):focus-visible ~ .rp-top .rp-dot:nth-child(1),
  .rp-r:nth-of-type(2):focus-visible ~ .rp-top .rp-dot:nth-child(2),
  .rp-r:nth-of-type(3):focus-visible ~ .rp-top .rp-dot:nth-child(3),
  .rp-r:nth-of-type(4):focus-visible ~ .rp-top .rp-dot:nth-child(4),
  .rp-r:nth-of-type(5):focus-visible ~ .rp-top .rp-dot:nth-child(5) {
    outline:2px solid var(--gold-dk); outline-offset:3px; border-radius:50%; }
  /* THE align-self:start THAT USED TO BE HERE IS GONE. It was added for the
     bundle, whose hero was a tall selector rather than a card, but it is
     written against .rp-top > .rp-short and so it fired on all ten single
     reports too — undoing the stretch .rp-top asks for two hundred lines
     up. Both pages have a hero card now and both want the same rule. */
  .rp-was { text-decoration:line-through; color:var(--ink-3); }
  .rp-save { margin:12px 0 0; font-size:13px; color:var(--good); }
  .rp-short .rp-save { margin-block:0 0; }
  .rp-sum { list-style:none; margin:0; padding:0; display:grid; gap:9px; }
  .rp-sum li { display:flex; align-items:baseline; gap:12px;
               justify-content:space-between; font-size:13px;
               color:var(--ink-2); }
  .rp-sum em { font-style:normal; font-variant-numeric:tabular-nums;
               color:var(--ink); }
  .rp-sum-t { padding-top:9px; border-top:1px solid var(--line-soft);
              color:var(--ink); font-weight:600; }
  .rp-sum-g em { color:var(--gold-dk); font-family:var(--font-serif);
                 font-size:17px; font-weight:600; }
  /* ── the phone ──────────────────────────────────────────────────────
     Last in the file: a media query adds no specificity, so a block written
     above the rules it overrides loses at equal weight. */
  @media (max-width:640px) {
    /* THE STATUS OF SOMETHING ALREADY PAID FOR. "Ready", "Being written",
       "Paid · being written", "Failed" — on the one page a customer opens
       BECAUSE they have already given us money, at 10px. */
    .rp-st { font-size:11px; }
    /* What each number on a detail page is: Sections, Format, Price, and
       "What it tells you". Three of the four are the answer to "is this
       worth 499 rupees". */
    .rp-short > span { font-size:11px; }
    /* The contents list's numbering, and the page number on each thumbnail
       of the sample — "1" of seven, in a corner of the image it belongs to. */
    .rp-secs li span { font-size:11px; }
    .rp-pk-n { font-size:11px; }
    /* "7 of 79 pages", which is the only thing in the reader saying how much
       of the report the sample is. */
    .rp-pv-c { font-size:11px; }
  }
  /* "← All reports" is the way back off a detail page and it sits alone on
     its own line, so the exemption for a link inline in a sentence does not
     reach it: it is a control, and it was 16px tall. Given the height here
     rather than in the coarse block below, because a back link that is easy
     to hit is not only a touch concern. */
  .rp-back a { display:inline-flex; align-items:center; min-height:44px; }
  /* Every control on these pages was short of 44: Download 39, See sample
     report 40, and inside the reader Open the PDF at 21, Get this report 39
     and Close 41. Open the PDF was less than half the rule.
     pointer:coarse rather than a width, because the size of a fingertip is
     not a property of the window. */
  @media (pointer:coarse) {
    .rp-act, .rp-see, .rp-pv-dl, .rp-pv-buy, .rp-pv-x { min-height:44px; }
    /* "Change", beside the delivery address. It shipped at 18px — the same
       mistake this whole block exists to correct, made again in a new
       place. Inline-flex so the box it is given is the box it fills. */
    .rp-mail-go { min-height:44px; display:inline-flex; align-items:center; }
    /* The carousel dots are 22px of box around an 8px mark, which is the
       right SIZE and the wrong TARGET. The mark stays; the box a finger has
       to find grows. */
    .rp-dot { width:44px; height:44px; }
    /* The two that are inline links rather than blocks need to be told to
       fill the height they have just been given. */
    .rp-act, .rp-see, .rp-pv-dl, .rp-pv-buy {
      display:inline-flex; align-items:center; }
  }

#rpf-all:checked ~ .c-cat-chips label[for="rpf-all"], #rpf-bundle:checked ~ .c-cat-chips label[for="rpf-bundle"], #rpf-personal:checked ~ .c-cat-chips label[for="rpf-personal"], #rpf-predictions:checked ~ .c-cat-chips label[for="rpf-predictions"], #rpf-relationships:checked ~ .c-cat-chips label[for="rpf-relationships"], #rpf-doshas_yogas:checked ~ .c-cat-chips label[for="rpf-doshas_yogas"] { border-color:var(--gold); background:var(--gold-bg); color:var(--gold-dk); }
#rpf-all:checked ~ .c-cat-chips label[for="rpf-all"] i, #rpf-bundle:checked ~ .c-cat-chips label[for="rpf-bundle"] i, #rpf-personal:checked ~ .c-cat-chips label[for="rpf-personal"] i, #rpf-predictions:checked ~ .c-cat-chips label[for="rpf-predictions"] i, #rpf-relationships:checked ~ .c-cat-chips label[for="rpf-relationships"] i, #rpf-doshas_yogas:checked ~ .c-cat-chips label[for="rpf-doshas_yogas"] i { color:var(--gold-dk); }
#rpf-bundle:checked ~ .c-cat-grid > :not(.c-cat-k-bundle),
#rpf-personal:checked ~ .c-cat-grid > :not(.c-cat-k-personal),
#rpf-predictions:checked ~ .c-cat-grid > :not(.c-cat-k-predictions),
#rpf-relationships:checked ~ .c-cat-grid > :not(.c-cat-k-relationships),
#rpf-doshas_yogas:checked ~ .c-cat-grid > :not(.c-cat-k-doshas_yogas) { display:none; }

  .rp-pub .d-main { margin-inline:auto; }
  /* Own prefix rx-. The grid, the chips and the card are all the shared
     component's and are not restyled here. */
  .rx-note { font-size:13.5px; line-height:1.62; color:var(--ink-2);
             max-width:74ch; margin:0 0 18px; }
#rpf-all:checked ~ .c-cat-chips label[for="rpf-all"] { border-color:var(--gold); background:var(--gold-bg); color:var(--gold-dk); }
#rpf-all:checked ~ .c-cat-chips label[for="rpf-all"] i { color:var(--gold-dk); }