/* ===========================================================================
   Qi Men Ahead
   Two palettes, both taken from the supplied designs:
     parchment  light, warm paper
     oxblood    dark, with a paper texture behind the board
   The board markup is generated in app.js; the class names here are the
   contract between the two. Cross-cell alignment inside the nine palaces is
   load-bearing, not cosmetic — see .flagrow and .pips.
   =========================================================================== */

:root{
  --ground:#F2ECE4; --panel:#FBF7F1; --sunk:#EDE6DC;
  --line:#DDD0C3; --line-soft:#E8DED2;
  --ink:#43201F; --dim:#8A6A5E; --faint:#A8917F;
  --accent:#8F6410; --accent-soft:#C6A15C; --accent-wash:#F5EAD3;
  /* The verdict only. The same gold, darkened: at 28px bold the accent
     counted as large text and 3:1 was the bar; at 16px semi-bold it is
     ordinary text and the bar is 4.5:1, which #8F6410 misses at 4.24:1.
     This is 5.41:1 on the answer bubble. --accent itself is untouched —
     everywhere else it is used it is larger, and already passes. */
  --verdict:#7A550E;
  /* --dim, corrected for small text. --dim is the product's colour for a
     secondary label and reads 3.94:1 on an answer bubble, under the 4.5:1 that
     anything below 18.66px needs. Same hue and saturation, darker: 5.66:1.
     Used where a quiet label is small and still has to be readable — the
     decision header's rows, and the line that reports an answer. --dim itself
     is untouched, everywhere else it is used. */
  --dim-aa:#6E5349;
  --plate-internal:#E7DCCB;
  --glass:rgba(251,247,241,.42); --glass-open:rgba(251,247,241,.62);
  --glass-edge:rgba(255,255,255,.5);
  --shadow:rgba(67,32,31,.20);
  /* The same linen the Oxblood theme uses, in its light form. A soft wash of
     the ground colour sits over it so panels and glyphs keep their contrast. */
  --texture:linear-gradient(rgba(242,236,228,.45), rgba(242,236,228,.55)),
            url('img/texture-parchment.jpg');
  --cjk:'Noto Serif TC','Songti TC','PingFang TC',serif;
  --ui:'Barlow Semi Condensed',system-ui,-apple-system,sans-serif;
  --radius:8px;

  /* Bottom-sheet stops. The collapsed height includes the iPhone home-indicator
     inset, so the composer and handle never sit under Safari's toolbar. */
  --safe-b:env(safe-area-inset-bottom, 0px);
  --brand-w:14px;                      /* the vertical brand strip beside the board */
  --brand-gap:6px;
  /* The collapsed sheet holds handle (44) + composer (52) + credit line (18) =
     114px. It was 104, so the credit line sat outside the sheet's own
     overflow:hidden and was being clipped.

     The lift then raises the composer off the bottom edge so it sits nearer
     the chart rather than leaving one large void between them. Collapsed
     content pins to the TOP of the sheet, so a taller sheet moves the composer
     up instead of stretching it. It is only taken where there is height to
     spare — a small phone keeps every pixel for the chart. Measured against
     dvh, not --vvh, so the on-screen keyboard never changes it. */
  --sheet-lift:clamp(0px, calc(100dvh - 720px), 64px);
  --sheet-collapsed:calc(114px + var(--sheet-lift) + var(--safe-b));
  /* Percentages of #app, NOT dvh. #app is sized from visualViewport, which
     iOS shrinks when the keyboard opens; dvh follows the LAYOUT viewport,
     which it does not. Using dvh here made the sheet 611px inside a 300px app
     with its top 311px above the screen, taking the conversation with it. */
  --sheet-partial:48%;
  --sheet-expanded:92%;
}
:root[data-theme="oxblood"]{
  --ground:#2A1618; --panel:rgba(51,25,28,.93); --sunk:rgba(36,19,21,.93);
  --line:#4B2C30; --line-soft:#3D2427;
  --ink:#EFE4DB; --dim:#AC9289; --faint:#7E6259;
  --accent:#D9A441; --accent-soft:#8A6428; --accent-wash:rgba(59,37,25,.9);
  /* 7.92:1 on the Oxblood bubble: it already clears 4.5:1, so the gold
     is kept exactly as it is. */
  --verdict:#D9A441;
  /* --dim on Oxblood is 6.13:1 on this bubble and needs no help, so this is
     exactly --dim: the correction is a Parchment problem only. */
  --dim-aa:#AC9289;
  --plate-internal:rgba(31,15,18,.93);
  --glass:rgba(51,25,28,.42); --glass-open:rgba(51,25,28,.62);
  --glass-edge:rgba(255,255,255,.12);
  --shadow:rgba(0,0,0,.45);
  --texture:linear-gradient(rgba(24,12,13,.45), rgba(24,12,13,.6)), url('img/texture-oxblood.jpg');
}

*{box-sizing:border-box}
html,body{margin:0;padding:0;-webkit-font-smoothing:antialiased}
body{background:var(--ground); color:var(--ink); font-family:var(--ui)}
[hidden]{display:none !important}
button{font:inherit; color:inherit; cursor:pointer}
button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{
  outline:2px solid var(--accent); outline-offset:1px;
}
.cjk{font-family:var(--cjk)}
.gloss{font-size:9px; line-height:1; letter-spacing:.07em; text-transform:uppercase; color:var(--faint)}
.sub{font-size:10.5px; color:var(--faint)}

#app{
  /* --vvh is set from visualViewport when the on-screen keyboard changes the
     visible area; dvh is the fallback. Without this the sheet stays anchored to
     a viewport the keyboard is covering and the composer becomes unreachable. */
  position:relative; width:100%; max-width:430px; height:var(--vvh, 100dvh); margin:0 auto;
  overflow:hidden; background:var(--ground); background-image:var(--texture);
  background-size:cover; background-position:center; isolation:isolate;
}
.col{position:relative; height:100%; display:flex; flex-direction:column}

/* ---------- drawer ---------- */
#backdrop{
  position:absolute; inset:0; background:rgba(0,0,0,.32); z-index:30;
  opacity:0; pointer-events:none; transition:opacity 280ms ease;
}
#backdrop.on{opacity:1; pointer-events:auto}
#sidebar{
  position:absolute; top:0; bottom:0; left:0; width:274px; z-index:40;
  background:var(--panel); border-right:1px solid var(--line);
  display:flex; flex-direction:column; overflow-y:auto;
  box-shadow:2px 0 16px rgba(0,0,0,.15);
  transform:translateX(-100%); transition:transform 320ms ease;
  padding-bottom:calc(10px + env(safe-area-inset-bottom));
}
#sidebar.on{transform:translateX(0)}
.side-head{
  padding:14px 14px 10px; display:flex; justify-content:space-between; align-items:center;
  border-bottom:1px solid var(--line-soft); flex-shrink:0;
}
.side-title{font-size:11.5px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--dim)}
.icon-btn{width:24px; height:24px; border:0; background:transparent; color:var(--dim);
  display:flex; align-items:center; justify-content:center; padding:0}
.newchat{
  margin:10px 12px; padding:9px 10px; border:1px dashed var(--accent-soft);
  border-radius:var(--radius); background:transparent; color:var(--accent);
  font-size:12px; font-weight:600; text-align:center; flex-shrink:0;
}
.history{padding:2px 8px 6px}
.hrow{padding:9px 10px; border-radius:var(--radius); margin-bottom:2px; background:transparent;
  border:0; width:100%; text-align:left; display:block}
.hrow.on{background:var(--sunk)}
.hrow .t{font-size:12.5px; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
.hrow .w{font-size:10px; color:var(--faint); margin-top:2px}
.history-empty{padding:4px 12px 10px; font-size:11.5px; color:var(--faint)}

.side-section{border-top:1px solid var(--line-soft); margin-top:8px; padding:12px 12px 4px}
.side-label{font-size:10px; font-weight:600; letter-spacing:.12em; text-transform:uppercase;
  color:var(--faint); margin-bottom:8px}
.side-row{margin:8px 0 12px}
.side-foot{margin-top:auto; padding:14px 12px 4px; text-align:center;
  font-size:10px; color:var(--faint)}

/* ---------- form controls ---------- */
.field{display:flex; flex-direction:column; gap:3px; margin-bottom:8px; min-width:0}
.field>span{font-size:9.5px; letter-spacing:.12em; text-transform:uppercase; color:var(--faint)}
.field input,.field select{
  padding:7px 9px; border:1px solid var(--line); border-radius:var(--radius);
  background:var(--ground); color:inherit; font:inherit; width:100%; min-width:0;
}
button.primary{background:var(--ink); color:var(--ground); border:1px solid var(--ink);
  border-radius:var(--radius); padding:8px 14px; font-weight:600}
/* Not element-qualified: a row in this menu may be a link as well as a
   button, and three of them are (Help & Support, Privacy Policy, Delete
   Account). An anchor is given the four things a button gets for free. */
.ghost{background:var(--ground); color:var(--dim); border:1px solid var(--line);
  border-radius:var(--radius); padding:8px 14px}
.wide{width:100%}
a.ghost{display:block; text-align:center; text-decoration:none; box-sizing:border-box}

/* ---------- top bar ---------- */
.topbar{
  height:44px; flex-shrink:0; display:flex; align-items:center; gap:9px;
  padding:0 10px; padding-top:env(safe-area-inset-top);
  height:calc(44px + env(safe-area-inset-top));
  border-bottom:1px solid var(--line); background:var(--panel);
  position:relative; z-index:10;
}
.burger{width:28px; height:28px; border:0; background:transparent; padding:0; flex-shrink:0;
  display:flex; flex-direction:column; justify-content:center; align-items:center; gap:3px}
.burger span{width:16px; height:2px; background:var(--ink); display:block}
.brand{font-size:14.5px; letter-spacing:.04em}
.brand-sub{font-size:9px; letter-spacing:.1em; text-transform:uppercase; color:var(--faint)}
.spacer{flex:1}
.round-btn{width:26px; height:26px; border-radius:50%; border:1px solid var(--line);
  background:var(--ground); color:var(--dim); padding:0; flex-shrink:0;
  display:flex; align-items:center; justify-content:center}

/* ---------- chart area ---------- */
#main{flex:1; overflow-y:auto; -webkit-overflow-scrolling:touch;
  padding:10px 9px calc(var(--sheet-collapsed-m, var(--sheet-collapsed)) + 10px);
  overscroll-behavior:contain;
  display:flex; flex-direction:column; align-items:center;
  /* Sit the chart under the top bar. Centring it in the leftover height
     looked balanced on paper, but on a tall phone it parked ~110px of dead
     space between the bar and the chart title. Slack belongs at the bottom,
     against the collapsed sheet, where it reads as breathing room. */
  justify-content:flex-start}
.empty{color:var(--faint); text-align:center; padding:52px 12px}
#chart{width:100%; display:flex; flex-direction:column; align-items:center}

.sheet-head{display:flex; justify-content:space-between; align-items:baseline;
  gap:6px; flex-wrap:wrap; width:100%; margin-bottom:6px}
.sheet-title{display:flex; align-items:baseline; gap:5px}
.sheet-title .mark{font-size:13px; letter-spacing:.04em}
.sheet-title .kind{font-size:7.5px; letter-spacing:.13em; text-transform:uppercase; color:var(--faint)}
.sheet-when{display:flex; align-items:baseline; gap:6px; background:transparent;
  border:0; padding:2px 0; border-bottom:1px dashed transparent}
.sheet-when:hover{border-bottom-color:var(--accent-soft)}
.sheet-when .stamp{font-size:9px; color:var(--dim); font-variant-numeric:tabular-nums}
.sheet-when .ju{font-size:10px; color:var(--accent)}

.stepper{display:flex; gap:8px; width:100%; margin-bottom:6px; flex-shrink:0}
.step{background:var(--panel); border:1px solid var(--line-soft); border-radius:999px;
  padding:8px 0; font-size:15px; color:var(--dim); flex:1;
  box-shadow:0 1px 3px rgba(0,0,0,.10)}
:root[data-theme="oxblood"] .step{box-shadow:0 1px 3px rgba(0,0,0,.3)}
.step.now{flex:2.2; background:var(--accent-wash); border:1.5px solid var(--accent-soft);
  color:var(--accent); font-size:12px; font-weight:700; letter-spacing:.12em}

.whenrow{display:flex; gap:6px; align-items:flex-end; width:100%; margin-bottom:8px}
.whenrow .field{flex:1; margin-bottom:0}

.pillars{display:grid; grid-template-columns:repeat(4,1fr); gap:1px;
  background:var(--line); border:1px solid var(--line); border-radius:4px;
  overflow:hidden; width:100%; flex-shrink:0}
.pil{background:var(--panel); height:70px; padding:7px 2px; display:flex;
  flex-direction:column; align-items:center; justify-content:space-between}
.pil .lab{font-size:7px; font-weight:600; letter-spacing:.1em; text-transform:uppercase; color:var(--dim)}
.pil .unit{display:flex; flex-direction:column; align-items:center; gap:3px}
.pil .g{font-family:var(--cjk); font-size:20px; line-height:1}
.pil .noble{color:var(--accent)}
.pil .gloss{display:none}

.board-label{display:flex; justify-content:space-between; width:100%; margin:8px 0 3px}
.board-label .lhs{font-size:7.5px; letter-spacing:.14em; text-transform:uppercase; color:var(--faint)}
.board-label .rhs{font-size:9.5px; color:var(--dim)}

/* ---------- the nine palaces ---------- */
/* The board keeps its own width; the brand sits beside it with a gap, so the
   board is never squeezed to make room for it. */
.board-row{display:flex; align-items:center; justify-content:center;
  gap:var(--brand-gap); width:100%}
/* Mirrors the brand on the other side so the board stays optically centred
   rather than being nudged left by the text beside it. */
.board-row::before{content:''; flex:0 0 var(--brand-w)}
/* Sits against the NW palace — the bottom of the board as it is drawn, with
   South at the top — rather than floating at the vertical midpoint. */
.brand-side{
  writing-mode:vertical-rl; text-orientation:mixed;
  width:var(--brand-w); flex:0 0 var(--brand-w);
  align-self:flex-end;
  font-size:9px; font-weight:700; letter-spacing:.14em; text-transform:lowercase;
  color:var(--dim); text-decoration:none; white-space:nowrap;
  text-align:end; -webkit-user-select:none; user-select:none;
}
.brand-side:hover{color:var(--ink)}

.board{
  display:grid; grid-template-columns:repeat(3,1fr);
  /* Equal rows are load-bearing: without this the centre cell makes the middle
     row taller than the other two, and E/W fall out of step with their
     neighbours. */
  grid-template-rows:repeat(3,1fr); gap:1px;
  background:var(--line); border:1px solid var(--line); border-radius:var(--radius);
  overflow:hidden; aspect-ratio:1; margin:10px 0 0; flex-shrink:0;
  /* leaves room for the brand strip and its mirror on a narrow phone */
  width:min(305px, calc(100% - 2 * (var(--brand-w) + var(--brand-gap))));
}
:root[data-theme="oxblood"] .board{border-color:rgba(255,255,255,.1)}
.cell{
  background:var(--panel); border:0; border-radius:0; padding:6px 5px 5px; text-align:left;
  display:flex; flex-direction:column; justify-content:space-between; gap:2px;
  font:inherit; color:inherit; min-width:0; overflow:hidden;
}
.cell.internal{background:var(--plate-internal)}
.cell.external{background:var(--panel)}
.cell.sel{box-shadow:inset 0 0 0 2px var(--accent)}
.cell.centre{background:var(--sunk); cursor:default; justify-content:center; align-items:center; gap:3px}

.chead{display:flex; align-items:center; justify-content:space-between; gap:3px; min-height:11px}
.chead .who{display:flex; align-items:center; gap:3px; min-width:0}
.chead .dir{font-size:8px; font-weight:700; color:var(--dim)}
.chead .gua{font-family:var(--cjk); font-size:10px; color:var(--dim)}
.chead .gloss{display:none}
.plate-tag{font-family:var(--cjk); font-size:7px; color:var(--accent); margin-left:1px}
.badges{display:flex; gap:2px; flex-shrink:0}
.badge{font-family:var(--cjk); font-size:7.5px; line-height:1.35; border-radius:2px;
  padding:0 3px; background:var(--accent); color:var(--panel)}
:root[data-theme="oxblood"] .badge{color:var(--ground)}

.triad{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:1px; text-align:center}
.triad.bottom{align-items:end}
.slot{display:flex; flex-direction:column; align-items:center; gap:1px; min-width:0}
.slot .g{font-family:var(--cjk); font-size:16px; line-height:1;
  display:inline-flex; align-items:baseline; justify-content:center; gap:1px}
/* A lodged (hidden) stem sits BESIDE the stem it shares a palace with, never
   on top of it. line-height:1 and baseline alignment keep it out of the line
   box, so it cannot shift the number and door row below. */
.hidden-stem{
  font-family:var(--cjk); font-size:9px; line-height:1; color:var(--accent);
  display:inline-flex; align-items:baseline; gap:.5px; white-space:nowrap;
}
.hidden-stem b{font-weight:400; font-size:6.5px; opacity:.75}
.slot .num{font-size:12px; line-height:1; font-weight:600; color:var(--dim);
  font-variant-numeric:tabular-nums}
.triad .gloss{display:none}

/* Always reserve three pip rows (9 is the largest palace number). Otherwise a
   1-3 pip palace has a shorter number block, and because the bottom row is
   bottom-aligned the door and stem glyphs ride higher than a 7-9 palace beside it. */
.pips{display:grid; grid-template-columns:repeat(3,2px); grid-auto-rows:2px;
  gap:1px; justify-content:center; align-content:start; height:8px}
.pips i{width:2px; height:2px; border-radius:50%; background:var(--faint); display:block}

/* One reserved badge height for the whole board — empty when a palace has no
   flags, tall enough for the busiest palace when it does. Set in app.js. */
.flagrow{height:var(--badge-h, 11px); display:flex; flex-wrap:wrap; gap:2px;
  justify-content:center; align-content:center; align-items:center}
.flag{font-family:var(--cjk); font-size:7.5px; line-height:1.3; color:var(--accent);
  border:1px solid var(--accent-soft); border-radius:2px; padding:0 2px}

.centre-stem{text-align:center}
.centre-stem .g{font-family:var(--cjk); font-size:20px; line-height:1}
.centre-rule{width:80%; height:1px; background:var(--line); margin:1px auto}
.centre-rows{width:100%; display:flex; flex-direction:column; gap:1px}
.crow2{display:flex; justify-content:space-between; gap:4px; align-items:baseline}
.crow2 .gloss{font-size:6.5px}
.crow2 .v{font-family:var(--cjk); font-size:9px; color:var(--dim)}

.board-hint{text-align:center; font-size:9px; color:var(--faint); margin:10px 0 4px}

/* ---------- palace detail ---------- */
.detail{width:100%; margin-top:10px; background:var(--panel); border:1px solid var(--line);
  border-radius:var(--radius); padding:10px 12px; font-size:12.5px}
.detail dl{display:grid; grid-template-columns:auto 1fr; gap:3px 10px; margin:0}
.detail dt{color:var(--faint); font-size:10px; letter-spacing:.06em; text-transform:uppercase}
.detail dd{margin:0}

/* ---------- reference panels (drawer) ---------- */
.panel{border-top:1px solid var(--line-soft)}
.panel summary{padding:10px 0; font-size:12px; color:var(--dim); cursor:pointer; list-style:none}
.panel summary::-webkit-details-marker{display:none}
.panel summary::before{content:"+ "; color:var(--faint)}
.panel[open] summary::before{content:"− "}
.panel>div{padding-bottom:12px; font-size:12px}
.panel p{margin:0 0 8px}
.caveat{color:var(--faint); font-size:11px}
.charttext{white-space:pre-wrap; font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:10.5px; line-height:1.5; max-height:300px; overflow:auto;
  background:var(--sunk); padding:8px; border-radius:4px}
.scenario-pick{margin-bottom:8px}
.scenario-pick select{width:100%; padding:7px 9px; border:1px solid var(--line);
  border-radius:var(--radius); background:var(--ground); color:inherit; font:inherit}
.dayhour{font-size:12px; margin-bottom:10px}
.dh-row{margin-bottom:3px}
.keystrip{display:flex; flex-direction:column; gap:6px; margin-bottom:10px}
.keys{display:flex; flex-wrap:wrap; gap:4px; align-items:center}
.keys .kname{font-size:9.5px; color:var(--faint); margin-right:4px}
.facts{font-family:var(--cjk); font-size:11px; color:var(--dim)}
.chartflag{font-family:var(--cjk); font-size:11px; color:var(--accent);
  border:1px solid var(--accent-soft); border-radius:2px; padding:1px 5px}

/* ---------- chat ---------- */
/* ---------- bottom sheet ----------
   Three discrete states, never an arbitrary position. The sheet is always
   anchored to the bottom of the app, so it can never be stranded off-screen.

   The handle row and the composer are the only children in COLLAPSED — the
   message log is display:none there. That is the fix for the trap: previously
   the log kept its full content height inside a fixed 129px overflow:hidden
   box, which pushed the handle above the panel and the composer hundreds of
   pixels below the screen. */
.chat{
  position:absolute; left:0; right:0; bottom:0; z-index:20;
  height:var(--sheet-collapsed);
  display:flex; flex-direction:column; justify-content:flex-start; overflow:hidden;
  background:transparent; border:0; border-radius:0;
  transition:height 340ms cubic-bezier(.22,.9,.32,1), background 340ms ease;
}
.chat.dragging{transition:none}          /* follow the finger, snap on release */

.chat[data-sheet="partial"],
.chat[data-sheet="expanded"]{
  border-top-left-radius:22px; border-top-right-radius:22px;
  background:var(--glass-open);
  backdrop-filter:blur(20px) saturate(160%); -webkit-backdrop-filter:blur(20px) saturate(160%);
  border:1px solid var(--glass-edge); border-bottom:none;
  box-shadow:0 -10px 34px var(--shadow), inset 0 1px 0 var(--glass-edge);
}
/* Collapsed is content-sized: the handle, the composer card and the credit
   line define it, so a taller composer can never be clipped by a stale magic
   number. --sheet-collapsed is only the floor (it carries the tall-phone
   lift); the measured height is what #main actually reserves. */
.chat[data-sheet="collapsed"]{height:auto; min-height:var(--sheet-collapsed)}
.chat[data-sheet="partial"]{height:var(--sheet-partial)}
.chat[data-sheet="expanded"]{height:var(--sheet-expanded)}
/* The guaranteed way back. Always rendered, always inside the sheet, always
   above the safe-area inset. A drag can also move the sheet, but this tap
   target is the fallback that cannot fail. */
.handle{
  padding:9px 0 7px; display:flex; flex-direction:column; align-items:center; gap:4px;
  flex-shrink:0; background:transparent; border:0; width:100%;
  touch-action:none;                      /* the handle drags the sheet, not the page */
  min-height:44px;                        /* comfortable tap target */
}
.handle:active .grip{background:var(--accent)}
.grip{width:34px; height:4px; border-radius:2px; background:var(--line); display:block}
.handle-label{font-size:10.5px; color:var(--dim); letter-spacing:.02em}
.chat[data-sheet="collapsed"] .handle-label{color:var(--accent)}
/* min-height:0 is load-bearing: without it a flex child refuses to shrink
   below its content and overflows the sheet. overscroll-behavior:contain stops
   a scroll at the end of the messages from chaining to the page. */
.log{
  flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden;
  -webkit-overflow-scrolling:touch;
  overscroll-behavior:contain; padding:2px 14px 8px;
  display:flex; flex-direction:column; gap:10px;
}
.chat[data-sheet="collapsed"] .log{display:none}
.chat-empty{text-align:center; color:var(--faint); font-size:12px; padding:14px 8px}

/* ---------- beginner empty state ----------
   Teaches by example, not by explaining the system. Quiet on purpose: text
   is the interface, no icons, no cards, nothing that reads as a dashboard
   tile or a help-centre article. */
.chat-empty.beginner{
  text-align:left; padding:18px 14px 8px;
  display:flex; flex-direction:column; gap:8px;
}
.be-title{
  font-size:15px; font-weight:600; color:var(--ink); letter-spacing:.01em;
  margin-bottom:2px;
}
.be-example{
  font:inherit; font-size:13px; line-height:1.5; text-align:left;
  padding:11px 13px; border-radius:var(--radius);
  border:1px solid var(--line); background:var(--panel); color:var(--ink);
  transition:border-color 160ms ease, background 160ms ease;
}
.be-example:hover{border-color:var(--accent-soft); background:var(--accent-wash)}
.be-example:focus-visible{outline:2px solid var(--accent); outline-offset:2px}

.turn{display:flex}
.turn.me{justify-content:flex-end}
.turn.them{justify-content:flex-start}
.bubble{max-width:82%; min-width:0; padding:8px 12px; font-size:13px; line-height:1.5;
  white-space:pre-wrap; overflow-wrap:anywhere}
.turn.me .bubble{border-radius:14px 14px 4px 14px; background:var(--ink); color:var(--ground)}
.turn.them .bubble{border-radius:14px 14px 14px 4px; background:var(--sunk); color:var(--ink)}
.turn.err .bubble{color:var(--accent)}
.turn .thinking{color:var(--faint); font-style:italic}

/* Long technical content — verses, formation names, claims — wraps rather than
   widening its container. The document must never gain horizontal width from
   chat or detail content. */
.disclose-body, .why, .tech, .detail, .detail dd{overflow-wrap:anywhere; min-width:0}
html, body{overflow-x:hidden}

/* Floor for EVERY focusable control, including any added later. Below 16px,
   iOS and WKWebView zoom the page on focus and do not zoom back — the bug
   that shifted the app sideways inside WhatsApp's browser. Individual rules
   may set a larger size; none may go under this. */
/* Text-entry controls only. Buttons never trigger the zoom, and forcing
   them would blow up the small pill controls. */
input, textarea, select{font-size:max(16px, 1em)}

/* The decision header: the verdict, the timing, the next step. Sits above the
   answer inside the same bubble and reads before it. Class names are "dhead"
   because "dh-row" already belongs to the Day/Hour panel above.

   THE VERDICT IS A CONCLUSION, NOT A HEADLINE. It was 28px, bold, upper case —
   which read as a machine announcing a result. It is now 16px at 600 in Title
   case, in the product's own face: one clear step above the reasoning, and
   nothing more. No pill, no badge, no box, no icon; the colour and the weight
   are the whole treatment.

   Timing and action sit quietly under it, one line each, a small grey label
   and a middle dot before the text. They are smaller than the reasoning
   because they are a summary of it, not part of it. */
.dhead{display:grid; gap:2px; margin:2px 0 11px; padding-bottom:11px; border-bottom:1px solid var(--line-soft)}
.dhead-label{font-size:16px; font-weight:600; line-height:1.3; color:var(--verdict); margin-bottom:5px}
/* The text itself keeps the bubble's own ink: it is a summary somebody has to
   be able to read, and greying it to make it quieter would cost more than it
   buys. Quieter is done with size and weight instead. */
.dhead-row{font-size:12.5px; line-height:1.45; font-weight:400; min-width:0; overflow-wrap:anywhere}
.dhead-k{text-transform:uppercase; font-size:10.5px; letter-spacing:.08em; color:var(--dim-aa)}
.dhead-dot{color:var(--dim-aa); margin:0 5px}

.disclose{display:flex; gap:6px; flex-wrap:wrap; margin-top:7px}
.disclose-btn{font-size:11px; padding:4px 10px; border-radius:999px;
  border:1px solid var(--line); background:var(--ground); color:var(--dim)}
.disclose-btn.on{border-color:var(--accent); color:var(--accent)}
.disclose-body{margin-top:7px; font-size:12.5px; line-height:1.55}

/* Was this useful? — deliberately quieter than the disclosure row above it.
   It sits at the bottom of an answer and must read as an aside, not an ask. */
.fb{display:flex; align-items:center; gap:6px; flex-wrap:wrap; margin-top:8px}
.fb-ask{font-size:11px; color:var(--faint)}
.fb-btn{font-size:13px; line-height:1; padding:3px 7px; border-radius:999px;
  border:1px solid transparent; background:none; opacity:.5}
.fb-btn:hover{opacity:1}
.fb-btn.on{opacity:1; border-color:var(--accent); background:var(--accent-wash)}
.fb-note{font-size:11px; color:var(--faint)}

.fb-panel{margin-top:7px; display:flex; flex-direction:column; gap:7px}
.fb-chips{display:flex; gap:6px; flex-wrap:wrap}
.fb-chip{font-size:11px; padding:4px 10px; border-radius:999px;
  border:1px solid var(--line); background:var(--ground); color:var(--dim); text-align:left}
.fb-chip.on{border-color:var(--accent); color:var(--accent); background:var(--accent-wash)}
.fb-text{font:inherit; font-size:12.5px; padding:7px 9px; border-radius:8px; resize:vertical;
  border:1px solid var(--line); background:var(--ground); color:inherit; width:100%}
.fb-actions{display:flex; gap:6px}
.fb-done, .fb-skip{font-size:11px; padding:4px 12px; border-radius:999px;
  border:1px solid var(--line); background:var(--ground); color:var(--dim)}
.fb-done{border-color:var(--accent); color:var(--accent)}
/* The report line. Quiet on purpose — it is there for the rare moment it is
   needed and must never compete with the answer — but it is a real control:
   underlined so it reads as something you can press, and in --dim rather than
   --faint so it can actually be read. 11px on --dim is the size and colour the
   feedback bar already uses for a word beside a control. */
.fb-report-row{margin-top:6px; font-size:11px; color:var(--dim-aa)}
.fb-report{font:inherit; font-size:11px; padding:0; border:0; background:none;
  color:var(--dim-aa); text-decoration:underline; text-underline-offset:2px; cursor:pointer}
.fb-report:disabled{opacity:.6; cursor:default}
.disclose-body .tech{font-size:11.5px}
.tech .trow{display:grid; grid-template-columns:96px 1fr; gap:2px 10px; padding:4px 0;
  border-top:1px solid var(--line-soft)}
.tech .tk{color:var(--faint); font-size:10px; letter-spacing:.06em; text-transform:uppercase}
.tech .sub{font-size:10.5px; color:var(--faint)}
.tech .pending{color:var(--accent); font-size:11px}
.tech .cjk{font-family:var(--cjk)}

/* One card: what you are writing sits on top, the send button on its own row
   beneath it. Roomier to type into than a single line with the button crowding
   the end of the text. */
.ask{flex-shrink:0; padding:8px 12px calc(8px + var(--safe-b))}
.chat[data-sheet="partial"] .ask,
.chat[data-sheet="expanded"] .ask{border-top:1px solid var(--glass-edge)}
.ask-box{
  border:1px solid var(--line); border-radius:20px; background:var(--panel);
  padding:11px 13px 9px; display:flex; flex-direction:column; gap:7px;
}
.chat[data-sheet="partial"] .ask-box,
.chat[data-sheet="expanded"] .ask-box{border-color:var(--glass-edge)}
.ask-box:focus-within{border-color:var(--dim)}
.ask textarea{
  width:100%; resize:none; border:0; background:transparent; padding:0;
  /* 16px is load-bearing, not taste. iOS Safari and every WKWebView — which
     is what WhatsApp's in-app browser is — ZOOM THE PAGE IN when a form
     control smaller than 16px takes focus, and never zoom back out on blur.
     That zoom is what shifted the app sideways and clipped Send, and no
     amount of scroll correction can undo it because it is a page scale, not
     a scroll. Keeping every focusable control at 16px means the zoom never
     happens. Do not drop this below 16px. */
  font-size:16px; line-height:1.4; font-family:inherit;
  color:var(--ink); max-height:96px; min-height:23px;
}
.ask textarea:focus{outline:none}
.ask-actions{display:flex; align-items:center; justify-content:flex-end}
.ask #send{width:34px; height:34px; border-radius:50%; background:var(--ink);
  color:var(--ground); border:0; padding:0; flex-shrink:0;
  display:flex; align-items:center; justify-content:center}
/* In oxblood the send button carried the same cream as the body text and read
   as a pale disc. It now takes the accent the theme already uses for its
   links — the same gold as "View plans" right above it — with the dark ground
   for the arrow. Both sizes; the light theme is unchanged. */
:root[data-theme="oxblood"] .ask #send{background:var(--accent); color:var(--ground)}
/* ---- question-limit notice ------------------------------------------------
   A quiet line above the composer, in the same panel/line/radius language as
   the ask box below it. Deliberately NOT a warning: no red, no icon colour
   change, no motion. It informs; the composer keeps working around it. */
.qnotice{
  flex-shrink:0; margin:0 12px 6px;
  border:1px solid var(--line-soft); border-radius:14px; background:var(--panel);
  padding:7px 10px; display:flex; align-items:center; gap:8px;
  font-size:12px; color:var(--dim); line-height:1.35;
}
.qnotice .qn-ico{flex-shrink:0; opacity:.75; display:flex}
.qnotice .qn-msg{flex:1 1 auto; min-width:0}
.qnotice .qn-cta{
  flex-shrink:0; background:transparent; border:0; padding:2px 2px;
  font:inherit; color:var(--accent); text-decoration:underline; cursor:pointer;
}
.qnotice .qn-cta:focus-visible{outline:2px solid var(--accent-soft); outline-offset:2px; border-radius:4px}
.qnotice .qn-x{
  flex-shrink:0; background:transparent; border:0; cursor:pointer;
  color:var(--faint); width:24px; height:24px; border-radius:50%;
  display:flex; align-items:center; justify-content:center; padding:0;
}
.qnotice .qn-x:hover{color:var(--dim)}
.qnotice .qn-x:focus-visible{outline:2px solid var(--accent-soft); outline-offset:1px}

/* The end-of-questions card. A continuation step, not an error dialog — it
   sits where the notice sits, so the finished answer above stays readable. */
.qnotice.qn-end{
  display:block; padding:13px 14px; border-color:var(--line);
  color:var(--ink);
}
.qnotice.qn-end h4{margin:0 0 6px; font-size:13.5px; font-weight:600; color:var(--ink)}
.qnotice.qn-end p{margin:0 0 7px; font-size:12px; color:var(--dim); line-height:1.5}
.qnotice.qn-end .qn-row{display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-top:10px}
.qnotice.qn-end .qn-primary{
  background:var(--ink); color:var(--ground); border:0; border-radius:999px;
  padding:8px 15px; font:inherit; font-size:12px; cursor:pointer;
}
.qnotice.qn-end .qn-secondary{
  background:transparent; border:0; padding:8px 4px; font:inherit; font-size:12px;
  color:var(--dim); cursor:pointer; text-decoration:underline;
}
.qnotice.qn-end .qn-primary:focus-visible,
.qnotice.qn-end .qn-secondary:focus-visible{outline:2px solid var(--accent-soft); outline-offset:2px}
.qnotice.qn-end .qn-when{font-size:11px; color:var(--faint); margin:8px 0 0}

/* On a phone the message takes the first row and the action drops beneath it,
   so nothing is squeezed and the close button stays reachable. */
@media (max-width: 430px){
  /* TWO rows, not three. The message keeps the first row with the icon and the
     close button — only the action drops beneath it, indented to line up under
     the text. Giving the message its own full-width row as well pushed the bar
     to three rows and ate more of the chat than a courtesy is worth. */
  .qnotice{flex-wrap:wrap; row-gap:2px}
  .qnotice .qn-ico{order:0}
  .qnotice .qn-msg{order:1; flex:1 1 auto; min-width:0}
  .qnotice .qn-x{order:2}
  .qnotice .qn-cta{order:3; flex:1 0 100%; text-align:left; margin-left:22px; padding:0}
}

.chat-note{flex-shrink:0; text-align:center; font-size:9.5px; color:var(--faint);
  padding:0 12px calc(6px + var(--safe-b))}
.chat[data-sheet="partial"] .chat-note,
.chat[data-sheet="expanded"] .chat-note{display:none}

@media (prefers-reduced-motion:reduce){*{transition:none !important; animation:none !important}}

/* ---------- account block in the drawer ---------- */
.side-account{border-top:1px solid var(--line-soft); margin-top:8px; padding:10px 12px 4px}
.acct-row{display:flex; justify-content:space-between; align-items:baseline; gap:8px; margin-bottom:9px}
.whoami{font-size:11px; color:var(--faint); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.credits{font-size:11px; font-weight:600; color:var(--accent); white-space:nowrap}
.credits.none{color:var(--dim)}

/* A finished conversation can be read but not continued. */
.readonly-note{
  flex-shrink:0; margin:0 12px calc(8px + env(safe-area-inset-bottom));
  padding:9px 12px; border:1px dashed var(--line); border-radius:var(--radius);
  font-size:11.5px; color:var(--faint); text-align:center;
}

/* ---------- Live Reading checklist ----------
   Shown while a forecast runs. Rows only tick on real backend events; the
   timer and captions are local and never advance the list. */
.live-reading{display:flex; flex-direction:column; gap:9px; min-width:210px}
.lr-head{font-size:12px; color:var(--dim); letter-spacing:.01em}
.lr-list{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:7px}
.lr-item{display:flex; align-items:flex-start; gap:8px; font-size:12.5px; line-height:1.35}
.lr-mark{width:12px; flex-shrink:0; text-align:center; font-size:11px; line-height:1.4}
.lr-body{display:flex; flex-direction:column; gap:2px; min-width:0}
.lr-label{color:var(--faint)}
.lr-caption{font-size:11px; color:var(--faint); opacity:.85}

.lr-item.lr-done .lr-mark{color:var(--accent)}
.lr-item.lr-done .lr-label{color:var(--ink)}
.lr-item.lr-active .lr-mark{color:var(--accent); animation:lr-pulse 1.6s ease-in-out infinite}
.lr-item.lr-active .lr-label{color:var(--ink)}
.lr-item.lr-pending .lr-mark{color:var(--line)}
/* A stopped row is what an in-flight step becomes on failure: visibly halted,
   never left pulsing. */
.lr-item.lr-stopped .lr-mark{color:var(--faint); animation:none}
.lr-item.lr-stopped .lr-label{color:var(--faint); text-decoration:line-through; text-decoration-thickness:1px}

.lr-timer{font-size:11px; color:var(--faint); font-variant-numeric:tabular-nums}

@keyframes lr-pulse{0%,100%{opacity:1}50%{opacity:.35}}
@media (prefers-reduced-motion:reduce){
  .lr-item.lr-active .lr-mark{animation:none}
}

/* ---------- history row options ---------- */
.hrow-wrap{display:flex; align-items:stretch; gap:2px}
.hrow-wrap .hrow{flex:1; min-width:0}
.hrow-more{
  flex:0 0 28px; border:0; background:transparent; color:var(--faint);
  border-radius:var(--radius); font-size:15px; line-height:1; padding:0;
}
.hrow-more:hover{background:var(--sunk); color:var(--dim)}
.hrow-confirm{
  margin:0 4px 8px; padding:10px 11px; border:1px solid var(--line);
  border-radius:var(--radius); background:var(--sunk);
}
.hc-q{font-size:12.5px; color:var(--ink); line-height:1.4}
.hc-s{font-size:11px; color:var(--faint); margin-top:3px; line-height:1.4}
.hc-a{display:flex; gap:6px; margin-top:9px}
.hc-a button{flex:1; padding:6px 0; font-size:12px; border-radius:var(--radius);
  border:1px solid var(--line); background:var(--ground); color:var(--dim)}
.hc-a .hc-go{border-color:var(--accent); color:var(--accent); font-weight:600}

/* ---------- welcome tour ----------
   A small card over a dimmed app, with a ring around the real control being
   described. Sits above the drawer so it is never hidden behind it. */
.wt-root{position:fixed; inset:0; z-index:60; display:flex; align-items:flex-end;
  justify-content:center; padding:0 14px calc(18px + var(--safe-b))}
.wt-scrim{position:absolute; inset:0; background:rgba(0,0,0,.55)}
.wt-spot{
  position:fixed; border-radius:12px; pointer-events:none;
  border:2px solid var(--accent);
  box-shadow:0 0 0 9999px rgba(0,0,0,.55), 0 0 18px rgba(217,164,65,.45);
  transition:top 220ms ease, left 220ms ease, width 220ms ease, height 220ms ease;
}
/* When the spotlight is showing, the ring's own shadow supplies the dimming. */
.wt-spot:not([hidden]) ~ .wt-scrim,
.wt-root:has(.wt-spot:not([hidden])) .wt-scrim{background:transparent}

.wt-card{
  position:relative; width:100%; max-width:360px;
  background:var(--panel); border:1px solid var(--line); border-radius:16px;
  padding:18px 18px 16px; box-shadow:0 20px 50px rgba(0,0,0,.45);
}
/* Move the card to the top when the highlighted control is low on screen. */
.wt-root:has(.wt-card-top){align-items:flex-start; padding-top:calc(18px + env(safe-area-inset-top))}

.wt-dots{display:flex; align-items:center; gap:6px; margin-bottom:12px}
.wt-dot{width:6px; height:6px; border-radius:50%; background:var(--line); display:block}
.wt-dot.on{background:var(--accent); width:18px; border-radius:3px}
.wt-count{margin-left:auto; font-size:10.5px; color:var(--faint);
  font-variant-numeric:tabular-nums}

.wt-title{font-size:17px; margin:0 0 8px; line-height:1.3; font-weight:600}
.wt-body{font-size:13.5px; line-height:1.6; color:var(--dim)}
.wt-body strong{color:var(--accent)}

.wt-actions{display:flex; gap:9px; align-items:center; margin-top:18px}
.wt-skip{flex:0 0 auto; padding:10px 16px; min-height:44px; font-size:13px;
  border:1px solid var(--line); border-radius:var(--radius);
  background:transparent; color:var(--dim)}
.wt-next{flex:1; padding:10px 16px; min-height:44px; font-size:14px; font-weight:600;
  border-radius:var(--radius)}

@media (prefers-reduced-motion:reduce){ .wt-spot{transition:none} }

/* ===========================================================================
   DESKTOP LAYOUT — >= 1200px only.
   ===========================================================================
   1024px was the first cut, then moved to 1200px: 1024 catches landscape
   iPad-sized tablets, which are meant to keep the mobile/tablet layout, not
   flip into a two-column desktop grid built for a mouse and a much wider
   canvas. Everything below this line is otherwise unchanged from that first
   pass — this is a breakpoint correction, not a redesign.

   Mobile and tablet are untouched below this line: every rule here is inside
   the media query, and every selector re-declares only the properties that
   need to change for a wide screen — the rest of each rule's declarations
   still come from its mobile definition above.

   The shape: the mobile view is a single narrow column with a chart on top
   and a chat panel that drags up over it as a bottom sheet. On desktop that
   becomes a real two-pane layout — chart on the left, conversation on the
   right, both visible together — using the exact same three children of
   `.col` (`.topbar`, `#main`, `.chat`) as CSS Grid areas. No HTML changed.

   The bottom-sheet mechanics (drag handle, collapsed/partial/expanded stops,
   the sheet's glass/blur styling) exist to let a chat panel share a phone
   screen with a chart it is overlapping. On desktop there is no overlap to
   manage, so those states are neutralised here rather than reused: the chat
   pane is always fully visible, a plain panel, laid out in normal flow. The
   underlying JS state machine (SHEET_STATE, setSheet, the drag listeners)
   keeps running exactly as before — it is simply no longer the thing that
   decides how tall or how visible the pane is on a desktop screen.

   The history/settings/chart-controls drawer is left as the same overlay
   burger menu on desktop as on mobile. It was not folded into a persistent
   left rail — that would mean rebuilding its slide-over/backdrop mechanics
   into static layout, which is a materially larger and riskier change than
   this task asked for. Everything in it stays one click away, unchanged.
   =========================================================================== */
@media (min-width:1200px){

  :root{
    /* The brand strip beside the board scales up a little with the bigger
       board below it — same variables the mobile rules already read, so nothing
       downstream needs a second override. */
    --brand-w:18px;
    --brand-gap:8px;
  }

  /* Fills the screen up to a comfortable reading width instead of the mobile
     430px card. The cap keeps an ultra-wide monitor from stretching the board
     and the chat column absurdly far apart — see the file header for why. */
  #app{max-width:1400px}

  /* The whole viewport wears the same texture as the app, so the calm margin
     beside a capped-width #app on a big screen reads as breathing room, not
     as a mobile page stranded on a plain background. */
  body{
    background-image:var(--texture); background-size:cover;
    background-position:center; background-attachment:fixed;
  }

  /* ---- the two-pane grid ---- */
  .col{
    display:grid;
    grid-template-columns:minmax(420px, 540px) 1fr;
    grid-template-rows:auto 1fr;
    grid-template-areas:"topbar topbar" "main chat";
  }
  .topbar{grid-area:topbar}
  #main{
    grid-area:main; min-width:0;
    padding:22px 22px 32px;               /* no bottom sheet to reserve room for */
  }
  .chat{grid-area:chat; min-width:0}

  /* ---- the chat pane: a plain static column, not a bottom sheet ----
     Same specificity as the mobile [data-sheet="…"] rules, later in the
     stylesheet, so these win for exactly the properties listed — everything
     else (flex layout, the log's own scrolling) is untouched and keeps
     working the same way it does on mobile. */
  .chat,
  .chat[data-sheet]{
    position:relative; height:100%;
    background:var(--panel); border-radius:0; box-shadow:none;
    backdrop-filter:none; -webkit-backdrop-filter:none;
    border:0; border-left:1px solid var(--line);
  }
  /* The drag handle is a touch affordance for a sheet that no longer moves.
     The state machine it drives (SHEET_STATE) is unaffected — dragging simply
     has no handle to start from at this width. */
  #chatHandle{display:none}

  /* The log, the composer and the credit line are always visible on desktop,
     never gated by which sheet stop the (now invisible) handle last landed
     on. Selectors match the mobile rules' specificity so source order alone
     decides the outcome. */
  .chat[data-sheet="collapsed"] .log,
  .chat[data-sheet="partial"] .log,
  .chat[data-sheet="expanded"] .log{display:flex}
  .chat[data-sheet="collapsed"] .chat-note,
  .chat[data-sheet="partial"] .chat-note,
  .chat[data-sheet="expanded"] .chat-note{display:block}
  /* The rule above the composer sat on .ask, which is the 680px column — so it
     ran 24px past each side of the notice and the composer box, both of which
     are the column minus its gutters. Drawn as an inset line instead, from the
     same two values, so all three share an edge. Phone rules are untouched:
     down there .ask spans the whole 430px column and the line already matched. */
  .chat[data-sheet="collapsed"] .ask,
  .chat[data-sheet="partial"] .ask,
  .chat[data-sheet="expanded"] .ask{border-top:0; position:relative}
  .chat[data-sheet="collapsed"] .ask::before,
  .chat[data-sheet="partial"] .ask::before,
  .chat[data-sheet="expanded"] .ask::before{
    content:''; position:absolute; top:0;
    left:var(--chat-gutter); right:var(--chat-gutter);
    border-top:1px solid var(--line);
  }
  .chat[data-sheet="collapsed"] .ask-box,
  .chat[data-sheet="partial"] .ask-box,
  .chat[data-sheet="expanded"] .ask-box{border-color:var(--line)}

  /* Content in the (now often much wider) chat pane stays a comfortable
     reading width rather than stretching bubbles edge to edge.

     Stated once, as two values, because four rules depend on agreeing about
     them. The composer's width is the one that was already right and is not
     being changed here — everything else is derived from it so the two can
     never drift apart again. */
  :root{
    --chat-col:680px;
    --chat-gutter:24px;
  }

  .log{max-width:var(--chat-col); margin:0 auto; width:100%; padding:18px var(--chat-gutter) 12px}
  .ask{max-width:var(--chat-col); margin:0 auto; width:100%; padding:14px var(--chat-gutter) 20px}
  .chat-note{max-width:var(--chat-col); margin:0 auto; padding:0 var(--chat-gutter) 12px}

  /* The question notice had no desktop rule at all, so it kept its phone
     margins and ran the full width of a pane that is far wider up here, while
     the composer sat neatly inside 680px. It is its own bordered box — there
     is no padded wrapper around it as there is around .ask-box — so it takes
     the column MINUS both gutters, which is exactly the composer's outer edge.
     Everything is border-box, so this width is the border edge. */
  .qnotice{
    max-width:calc(var(--chat-col) - var(--chat-gutter) * 2);
    width:100%; margin-left:auto; margin-right:auto;
  }

  /* "slide the panel up to ask" describes a gesture desktop no longer has. */
  .board-hint{display:none}

  /* ---- the chart, larger and easier to read ----
     Only the properties that actually need to grow; every other declaration
     — grid structure, colours, borders — still comes from the mobile rule. */
  .board{width:min(520px, calc(100% - 2 * (var(--brand-w) + var(--brand-gap))))}
  .brand-side{font-size:10px}

  .pillars .pil{height:86px}
  .pillars .pil .lab{font-size:8px}
  .pil .g{font-size:24px}

  .board-label .lhs{font-size:9px}
  .board-label .rhs{font-size:11px}

  .cell{padding:9px 7px 7px}
  .chead .dir{font-size:9px}
  .chead .gua{font-size:11px}
  .badge{font-size:9px; padding:0 4px}
  .flag{font-size:9px; padding:0 3px}

  .slot .g{font-size:19px}
  .hidden-stem{font-size:11px}
  .hidden-stem b{font-size:8px}
  .slot .num{font-size:14px}

  .centre-stem .g{font-size:24px}
  .crow2 .v{font-size:10.5px}

  .sheet-title .mark{font-size:15px}
  .sheet-title .kind{font-size:9px}
  .sheet-when .stamp{font-size:10.5px}
  .sheet-when .ju{font-size:11.5px}
}

/* ===========================================================================
   PLAN & BILLING
   A panel over the existing drawer, in the app's own palette. Sized for a
   phone first: Daniel sends /plans by WhatsApp, so the whole billing
   experience has to be usable one-handed on a small screen.
   =========================================================================== */
dialog.billing-dlg{
  border:1px solid var(--line); border-radius:14px; background:var(--panel);
  color:var(--ink); padding:0; width:min(430px, calc(100vw - 24px));
  max-height:min(88dvh, 760px); overflow:hidden;
  box-shadow:0 22px 60px var(--shadow);
}
dialog.billing-dlg::backdrop{background:rgba(0,0,0,.5)}
.billing-head{display:flex; align-items:center; gap:10px; padding:13px 15px;
  border-bottom:1px solid var(--line)}
.billing-head strong{font-size:14px; letter-spacing:.02em; flex:1}
.billing-head .ghost{padding:5px 11px; font-size:12px; border-radius:var(--radius)}
.billing-body{padding:14px 15px calc(18px + env(safe-area-inset-bottom));
  max-height:calc(88dvh - 52px); overflow:auto; -webkit-overflow-scrolling:touch}

.bsec{margin-bottom:16px}
.bsec h4{margin:0 0 8px; font-size:9.5px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--faint); font-weight:600}
.brow{display:flex; justify-content:space-between; gap:12px; font-size:13.5px;
  padding:5px 0; border-bottom:1px solid var(--line-soft)}
.brow:last-child{border-bottom:0}
.brow .k{color:var(--dim)}
.brow .v{text-align:right; font-variant-numeric:tabular-nums}
.brow.accent .v{color:var(--accent)}
.bnote{font-size:12px; color:var(--faint); line-height:1.6; margin:9px 0 0}

.bactions{display:flex; flex-direction:column; gap:7px; margin-top:4px}
.bwide{width:100%; padding:11px 13px; border-radius:var(--radius); font-size:13.5px}
.bwide.danger{color:var(--accent); border-color:var(--accent-soft)}

.bopt{display:flex; flex-direction:column; align-items:flex-start; gap:2px; width:100%;
  text-align:left; padding:11px 13px; margin-bottom:7px; border-radius:var(--radius);
  border:1px solid var(--line); background:var(--ground); color:var(--ink)}
.bopt[disabled]{opacity:.45; cursor:not-allowed}
.bopt .t{font-size:13.5px}
.bopt .d{font-size:11.5px; color:var(--faint)}

.bwarn{border:1px solid var(--accent-soft); background:var(--accent-wash);
  border-radius:var(--radius); padding:11px 13px; font-size:12.5px; line-height:1.6;
  margin-bottom:14px}
.bwarn strong{display:block; margin-bottom:3px}

.bmsg{font-size:12.5px; color:var(--dim); min-height:1.2em; margin:6px 0 0}
.bmsg.bad{color:var(--accent)}

/* ── Boundary notices and What's new ──────────────────────────────────────────
   The same tokens as everything else, so these inherit both themes for free.
   Deliberately NOT an error style: a boundary is not a failure, and a red box
   would tell the customer they did something wrong when they did not. The card
   is set apart by a quiet accent rule and a wash, enough to be noticed and no
   more. */
.notice-card{
  border:1px solid var(--line); border-left:2px solid var(--accent-soft);
  border-radius:var(--radius); background:var(--accent-wash);
  padding:11px 13px; margin:10px 0 0;
}
.notice-card + .notice-card{margin-top:9px}
.notice-title{
  font-size:12.5px; font-weight:600; letter-spacing:.01em; color:var(--ink);
  margin-bottom:4px;
}
.notice-body{
  margin:0; font-size:12.5px; line-height:1.65; color:var(--dim);
  text-wrap:pretty;
}
/* Inside a chat turn the card sits under the refusal it explains, indented to
   the same edge so it reads as part of the same reply rather than a new one. */
.turn .notice-card{margin-top:11px; max-width:100%}

.notice-dlg .notice-panel{display:flex; flex-direction:column}
.notice-dlg .notice-card:first-child{margin-top:0}
.notice-date{
  font-size:9.5px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--faint); font-weight:600; margin-bottom:10px;
}

@media (max-width:480px){
  .notice-card{padding:10px 12px}
  .notice-title, .notice-body{font-size:12px}
}

/* ── Behavioural card ────────────────────────────────────────────────────────
   Secondary to the reading above it, and it must look it: no accent rule, no
   wash, a hairline top border and the muted type used for supporting text.
   Somebody skimming their answer should be able to ignore this entirely. */
.bcard{
  margin-top:14px; padding-top:12px; border-top:1px solid var(--line-soft);
}
.bcard-wrap{
  margin:0 0 8px; font-size:11px; letter-spacing:.02em; color:var(--faint);
}
.bcard-prompt{
  margin:0 0 10px; font-size:12.5px; line-height:1.6; color:var(--dim);
  text-wrap:pretty;
}
.bcard-opts{display:flex; flex-wrap:wrap; gap:8px}
.bcard-opt{
  flex:1 1 auto; min-width:0; text-align:left;
  padding:9px 12px; font-size:12.5px; line-height:1.45;
  background:var(--ground); color:var(--ink);
  border:1px solid var(--line); border-radius:var(--radius); cursor:pointer;
}
.bcard-opt:hover{border-color:var(--accent-soft)}
.bcard-skip{
  margin-top:9px; padding:2px 0; background:none; border:0;
  font-size:11.5px; color:var(--faint); cursor:pointer; text-decoration:underline;
}
.bcard-skip:hover{color:var(--dim)}
.bcard-done{margin:0; font-size:12px; color:var(--faint)}

@media (max-width:480px){
  .bcard-opts{flex-direction:column}
  .bcard-opt{width:100%}
}
