* { 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 square ─────────────────────────────────────────────────── */
  .k-top { display:grid; gap:22px 26px; grid-template-columns:1fr;
           align-items:start; }
  @media (min-width:900px) {
    /* auto / 1fr, not auto / auto. The square spans both rows and is taller
       than the picker and the numbers stacked, and with two auto rows that
       surplus was split between them — opening a gap under the picker that
       grew and shrank with the length of the working. Sending it all to the
       1fr row puts the slack under the numbers, where nothing is looking. */
    .k-top { grid-template-columns:minmax(0,400px) 1fr;
             grid-template-rows:auto 1fr; }
    .k-form   { grid-area:1 / 2; }
    .k-sqwrap { grid-area:1 / 1 / span 2 / 2; }
    .k-side   { grid-area:2 / 2; }
  }

  .k-sq { position:relative; aspect-ratio:1; width:100%; max-width:400px;
          margin:0 auto; border:1px solid var(--line); border-radius:var(--r-card);
          background:var(--surface); overflow:hidden; }
  /* Ruled, not gapped. With no gutters the three cell centres sit exactly on
     sixths of the box, so the SVG overlay lines up with the HTML without a
     single measurement — and a magic square is a ruled square anyway. */
  /* ── ABSOLUTE, NOT height:100% — THE SQUARE COLLAPSED ON EVERY IPHONE ──
     .k-sq takes its height from `aspect-ratio` and nothing else, and WebKit
     does not treat that height as definite when a child resolves a
     PERCENTAGE against it. So `height:100%` fell back to auto; every cell's
     content is position:absolute, so the three rows had nothing in flow and
     computed to zero, and all nine cells stacked at the top of the box with
     their labels printed over one another. Fixed in WebKit only for Safari
     27 — every iPhone in use is affected. Reported on the signed-in copy of
     this square, /home/numerology/, and this file had the same construction.
     Absolute + inset:0 sizes from the containing block's used box rather
     than a percentage, which is how the sibling .k-svg has always been laid
     out and why the arrows were the one thing still the right size.
     Chrome renders the broken version correctly, so no headless check can
     see it — checks/loshu.mjs holds the construction instead. */
  .k-grid { position:absolute; inset:0; z-index:1; display:grid;
            grid-template-columns:repeat(3,minmax(0,1fr));
            grid-template-rows:repeat(3,minmax(0,1fr)); }
  /* SYMMETRY IS THE WHOLE JOB HERE.
     The first version sized the digits by how many there were — 40px for one,
     33px for two, 21px for an absent one in a ring. Nine cells, four different
     type sizes, digits at four different heights: the square came out looking
     broken rather than composed. So nothing about a cell's geometry depends on
     its contents any more. One type size everywhere, one fixed slot per digit,
     the digits centred in the top half and the label centred in the bottom
     half of every cell without exception. */
  .k-cell { position:relative; min-width:0;
            border-right:1px solid var(--line);
            border-bottom:1px solid var(--line); }
  .k-cell:nth-child(3n) { border-right:0; }
  .k-cell:nth-child(n+7) { border-bottom:0; }

  /* Top half. The cell centre is the boundary between the digits and the
     label, which is exactly where the arrows run — so a complete line has a
     clear channel through every cell it crosses and never strikes a glyph.
     (An arrow only ever crosses cells that are present, by definition.) */
  .k-cell b { position:absolute; top:0; bottom:50%; left:50%; width:96px;
              transform:translateX(-50%);
              display:flex; flex-wrap:wrap; gap:4px 5px;
              align-content:center; justify-content:center;
              font-family:var(--font-serif); font-weight:600; line-height:1;
              font-size:27px; color:var(--ink);
              font-variant-numeric:tabular-nums; }
  /* A fixed slot per digit, so four 1s line up with four 8s and a lone 9 sits
     dead centre. 96px holds four; the worst date in 130 years has seven, which
     wraps to 4 + 3 and still fits the half-cell. */
  .k-cell b span { width:19px; text-align:center; }

  /* Bottom half, centred in it — so every label in a row shares a baseline
     whether it runs to one line or three. */
  .k-cell i { position:absolute; top:50%; bottom:0; left:2px; right:2px;
              display:flex; align-items:center; justify-content:center; }
  /* 15ch is measured, not guessed: it is the width at which all nine labels
     break into exactly two lines — the longest, MARRIAGE & RELATIONSHIP, and
     the shortest, HEALTH & FAMILY, alike. One line here and three there is the
     same ragged look the digit sizes were causing. (12ch put the ampersand on
     a line of its own in three cells: `ch` is the advance of a zero and does
     not count the .07em letter-spacing, so the box was narrower than the
     character count suggested. checks/loshu.mjs now asserts the line count.)
     The backdrop is what a diagonal arrow passes behind. A line through a
     40px numeral reads as depth; a line through 8.5px letter-spaced mono
     reads as a mistake. */
  .k-cell i span { max-width:15ch; padding:2px 4px; border-radius:4px;
                   background:var(--surface);
                   font-style:normal; font-family:var(--font-mono);
                   font-size:8.5px; letter-spacing:.07em;
                   text-transform:uppercase; color:var(--ink-3);
                   line-height:1.45; text-align:center; }
  .k-absent i span { background:var(--rail); }

  /* Absent keeps the digit at full size and full contrast — a faded one was
     1.9:1 against the cell, i.e. not readable, and there are usually three.
     The dashed rule sits on the cell instead of round the numeral, so an empty
     cell is unmistakable without being a different shape from a full one. */
  .k-absent { background:var(--rail); }
  .k-absent::before { content:''; position:absolute; inset:6px;
                      border:1px dashed var(--line); border-radius:9px; }
  .k-off { color:var(--ink-3); }

  .k-svg { position:absolute; inset:0; z-index:0; width:100%; height:100%;
           overflow:visible; }
  .k-arrow { stroke:var(--gold); stroke-width:2.5; stroke-linecap:round;
             opacity:.6; }
  .k-head { fill:var(--gold); opacity:.6; }

  /* ── the working ────────────────────────────────────────────────── */
  /* The trigger stretches to its column, and this column is the wide one. */
  .k-form { max-width:340px; }
  .k-form .c-pf { margin-bottom:0; }
  .k-bad { margin:10px 0 0; font-size:13px; color:var(--bad); }
  .k-leg { display:flex; flex-wrap:wrap; gap:6px; justify-content:center;
           margin-top:10px; }

  .k-nums { display:grid; gap:12px; grid-template-columns:1fr; margin-bottom:16px; }
  @media (min-width:520px) { .k-nums { grid-template-columns:repeat(2,1fr); } }
  .k-num { min-width:0; padding:16px 18px; border:1px solid var(--line);
           border-radius:var(--r-card); background:var(--surface); }
  .k-num b { display:block; font-family:var(--font-serif); font-size:38px;
             font-weight:600; line-height:1; color:var(--gold-dk); margin:6px 0 4px; }
  .k-num i { display:block; font-style:normal; font-family:var(--font-mono);
             font-size:11px; color:var(--ink-2); line-height:1.5;
             word-break:break-word; }
  .k-num em { display:block; font-style:normal; font-size:12px;
              color:var(--ink-3); margin-top:6px; }

  .k-tally { padding:16px 18px; border:1px solid var(--line);
             border-radius:var(--r-card); background:var(--surface); }
  .k-counts { display:grid; gap:14px; grid-template-columns:repeat(2,1fr); }
  /* .k-tally > div > div reached .k-counts' children, not the chip rows —
     the labels ended up beside the chips rather than above them. */
  .k-counts > div > div { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
  .k-arrows { margin-top:16px; padding-top:14px; border-top:1px solid var(--line); }
  .k-arrows b { color:var(--gold-dk); font-weight:inherit; }
  .k-arrows > div { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
  .k-none { font-size:13px; color:var(--ink-3); }
  .k-rep { font-variant-numeric:tabular-nums; }
  .k-rep sup { font-size:9px; margin-left:1px; color:var(--ink-3); }
  .k-miss { border-style:dashed; color:var(--ink-3); }
  .k-arr { background:var(--gold-bg); border-color:var(--gold-lt);
           color:var(--ink); }

  /* ── the reading ────────────────────────────────────────────────── */
  .k-reads { display:grid; gap:12px; grid-template-columns:1fr; }
  @media (min-width:760px) { .k-reads { grid-template-columns:repeat(2,1fr); } }
  @media (min-width:1160px) { .k-reads { grid-template-columns:repeat(3,1fr); } }
  .k-read { min-width:0; padding:18px 20px; border:1px solid var(--line);
            border-radius:var(--r-card); background:var(--surface); }
  .k-read-h { display:flex; align-items:center; gap:14px; margin-bottom:10px; }
  .k-read-n { flex:none; width:44px; height:44px; display:grid; place-items:center;
              border-radius:50%; border:1px solid var(--line);
              font-family:var(--font-serif); font-size:22px; font-weight:600;
              color:var(--ink); background:var(--rail); }
  .k-read-h b { display:block; font-size:14.5px; font-weight:600;
                color:var(--ink); margin-top:2px; line-height:1.35; }
  .k-read p { font-size:13.5px; line-height:1.65; color:var(--ink-2); margin:0; }
  /* The tier reads as the sharper half of the same paragraph, not as a
     separate note — it qualifies the sentence above it. Darker than the
     body copy so it is the line the eye lands on when a digit repeats. */
  .k-read .k-read-t { margin-top:8px; color:var(--ink); font-weight:600; }
  .k-read .k-read-t[hidden] { display:none; }
  /* Present, repeated and absent are three different readings of the same
     digit, so the card says which one you are looking at without a legend.
     No red or green: absence here is a thing to work on, not a fault. */
  .k-r-multiple .k-read-n { background:var(--gold-bg); border-color:var(--gold-lt);
                            color:var(--gold-dk); }
  .k-r-absent .k-read-n { border-style:dashed; color:var(--ink-3);
                          background:transparent; }
  .k-r-absent { background:var(--rail); }

  /* ── the arrows ─────────────────────────────────────────────────── */
  .k-cat { margin-bottom:26px; }
  .k-cat-h h3 { font-family:var(--font-serif); font-size:19px; font-weight:600;
                margin:0 0 6px; color:var(--ink); }
  .k-cat-h p { font-size:13.5px; line-height:1.6; color:var(--ink-3);
               max-width:74ch; margin:0 0 14px; }
  .k-planes { display:grid; gap:12px; grid-template-columns:1fr; }
  @media (min-width:760px) { .k-planes { grid-template-columns:repeat(3,1fr); } }
  .k-plane { min-width:0; display:flex; gap:16px; align-items:flex-start;
             padding:16px 18px; border:1px solid var(--line);
             border-radius:var(--r-card); background:var(--rail); }
  .k-plane.k-on { background:var(--surface); border-color:var(--gold-lt); }
  .k-plane-b { min-width:0; }
  .k-pnm { display:block; font-size:14px; font-weight:600; color:var(--ink);
           margin:3px 0 8px; }
  .k-pst { font-weight:inherit; color:var(--ink-3); }
  .k-on .k-pst { color:var(--gold-dk); }
  .k-plane p { font-size:13px; line-height:1.6; color:var(--ink-2); margin:0; }

  /* The thumbnail square. Same 3x3, same reading, 22px cells — so the shape of
     an arrow is something you see rather than something you decode from
     "8-1-6 · Bottom Row". */
  .k-mini { flex:none; display:grid; grid-template-columns:repeat(3,22px);
            grid-template-rows:repeat(3,22px); gap:2px; }
  .k-mini span { display:grid; place-items:center; border-radius:4px;
                 background:var(--surface); border:1px solid var(--line-soft);
                 font-family:var(--font-mono); font-size:10px;
                 color:var(--ink-3); }
  .k-plane .k-mi-on { background:var(--gold-bg); border-color:var(--gold-lt);
                      color:var(--gold-dk); }
  /* A complete arrow fills its three; a broken one only outlines them, so the
     two states differ in weight and not only in hue. */
  .k-on .k-mi-on { background:var(--gold-solid); border-color:var(--gold-solid);
                   color:var(--gold-solid-text); }

  /* ── the method ─────────────────────────────────────────────────── */
  .k-steps { list-style:none; margin:0; padding:0; display:grid; gap:14px;
             max-width:78ch; }
  .k-steps li { display:flex; gap:14px; align-items:flex-start; }
  .k-step-n { flex:none; width:26px; height:26px; display:grid; place-items:center;
              border-radius:50%; background:var(--gold-bg); color:var(--gold-dk);
              font-family:var(--font-mono); font-size:11px; }
  /* .k-steps b also matched the <b> inside each step's prose — "571990" and
     "4" broke onto lines of their own, mid-sentence. Element selectors under
     a component class keep doing this; the title gets a class of its own. */
  .k-step-t { display:block; font-size:14.5px; color:var(--ink); margin-bottom:3px; }
  .k-steps p { font-size:13.5px; line-height:1.6; color:var(--ink-2); margin:0; }
  .k-magic { margin:20px 0 0; padding:14px 16px; border-radius:12px;
             background:var(--gold-bg); font-size:13.5px; line-height:1.65;
             color:var(--ink-2); max-width:78ch; }

/* The phone, last in the file. The caption in each cell of the Lo Shu square
   — "Money & Property", "Reputation & Fame", "Marriage & Relations" — at
   8.5px, which is what every number in the grid actually means; and the mini
   grid's digits at 10px. */
@media (max-width:640px) {
  .k-cell i span, .k-mini span, .k-mi-on { font-size:11px; }
}

.c-seo { display:grid; gap:16px; grid-template-columns:1fr; max-width:78ch; }
@media (min-width:900px) { .c-seo { grid-template-columns:repeat(2,1fr);
                                    max-width:none; } }
.c-seo-s h3 { font-family:var(--font-serif); font-size:18px; font-weight:600;
              color:var(--ink); margin:0 0 7px; }
.c-seo-s p { font-size:13.5px; line-height:1.7; color:var(--ink-2); margin:0;
             max-width:72ch; }