/* kx_hub.css — DESIGN PASS 2c section E.   🆕 P1-20E  2026-09-26
 *
 * One request instead of eight. This file is the VERBATIM concatenation, in browser
 * order, of the sheets listed below plus the page's own inline <style>. Nothing was
 * reordered, rewritten or de-duplicated, so the cascade result is identical by
 * construction; the old versioned files stay on disk and are simply no longer linked.
 *
 *   css/v2/kx_globe.css
 *   css/v1/kx_vt.css
 *   css/v3/kx_hub2.css
 *   css/v4/kx_layout.css
 *   css/v5/kx_fix.css
 *   css/v6/kx_hero.css
 *   css/v8/kx_order.css
 *   kanji_hub.php inline <style> (lines 134-273)
 *   css/v9/kx_a11y.css
 */

/* ==========================================================================
   css/v2/kx_globe.css
   ========================================================================== */
/* kx_globe.css v2 — D5 hub kanji globe + search transition.                 🆕 P1-17
 * The globe is decoration with a job: it shows that the dictionary is full of real kanji and
 * that most of them are in Umesh's courses. It must never get in the way of the search box.
 */
.kx-globe-box{position:relative;width:min(92vw,460px);aspect-ratio:1;margin:2px auto -34px;
  touch-action:none;cursor:grab;z-index:1}
.kx-globe-box:active,.kx-globe-box.drag{cursor:grabbing}
.kx-globe-box canvas{width:100%;height:100%;display:block}
.kx-globe-box[hidden]{display:none}
@media (max-width:480px){.kx-globe-box{width:96vw;margin-bottom:-26px}}

/* the search box has to sit above the globe, never under it */
.sbox,.modes{position:relative;z-index:3}

.kx-legend{font-size:12px;color:var(--muted);text-align:center;margin:2px 0 0}
.kx-legend i{display:inline-block;width:9px;height:9px;border-radius:50%;
  background:var(--red,#ed1f24);margin-right:6px;vertical-align:-1px}

/* the character that flies to the page — also the view-transition anchor */
.kx-flyer{position:fixed;left:0;top:0;z-index:60;pointer-events:none;display:none;
  font-family:var(--font-display,serif);font-weight:700;color:var(--red,#ed1f24);
  line-height:1;transform-origin:0 0}
.kx-flyer.on{display:block}

@media (prefers-reduced-motion:reduce){
  .kx-globe-box{cursor:default}
}

/* ==========================================================================
   css/v1/kx_vt.css
   ========================================================================== */
/* kx_vt.css v1 — the cross-document View Transition between /kanji and /kanji/<char>.
 * Kept in its own file because BOTH pages must opt in for a cross-document transition to
 * happen at all, and neither page should carry the other's rules.
 * Browsers without support simply ignore all of this and get the scripted fade instead.
 */
@view-transition { navigation: auto; }

::view-transition-old(kx-hero),
::view-transition-new(kx-hero){ animation-duration:.42s; animation-timing-function:cubic-bezier(.2,.8,.2,1) }

@media (prefers-reduced-motion:reduce){
  @view-transition { navigation: none; }
}

/* ==========================================================================
   css/v3/kx_hub2.css
   ========================================================================== */
/* kx_hub2.css v3 — hub: stop clipping the globe, and give it a desktop.     🛠️ P1-19
 *
 * BUG (Umesh, real screens): the Type/Draw toggle overlapped the bottom of the globe,
 * because v2 pulled the search block up with margin-bottom:-34px on the globe box. The
 * overlap is gone; the search block now sits BELOW the globe with real spacing at every
 * width, and the globe is never cropped.
 *
 * Mobile is unchanged apart from removing that overlap. Desktop gets the hero treatment.
 */

/* ---------- all widths: nothing overlaps the globe ---------- */
.kx-globe-box{
  width:min(92vw, 440px);
  margin:4px auto 10px;            /* 🛠️ was -34px, which is what clipped it */
  aspect-ratio:1;
}
.sbox,.modes{position:relative;z-index:3}
.kx-legend{margin:0 0 14px}

/* ---------- ≥900: the globe becomes the centrepiece ---------- */
@media (min-width:900px){
  .wrap{max-width:1180px}
  .kx-hero-grid{
    display:grid;
    grid-template-columns:minmax(0,1fr) minmax(320px,420px);
    gap:clamp(24px,3vw,48px);
    align-items:center;
    min-height:calc(100vh - 190px);
  }
  .kx-hero-grid > .kx-globe-col{grid-column:1;justify-self:center;width:100%}
  .kx-hero-grid > .kx-search-col{grid-column:2;align-self:center}
  .kx-globe-box{width:min(78vh, 52vw); max-width:620px; margin:0 auto}
  .kx-legend{text-align:center}
  h1.hub-h1{font-size:clamp(34px,3.4vw,52px)}
}

/* ---------- ≥1280 ---------- */
@media (min-width:1280px){
  .wrap{max-width:1320px}
  .kx-globe-box{width:min(82vh, 55vw); max-width:720px}
  .kx-hero-grid{grid-template-columns:minmax(0,1fr) minmax(360px,460px)}
}

/* ---------- ≥1600 ---------- */
@media (min-width:1600px){
  .wrap{max-width:1520px}
  .kx-globe-box{max-width:820px}
  .kx-hero-grid{grid-template-columns:minmax(0,1fr) minmax(420px,520px)}
}

/* ---------- draw mode on desktop: pad beside the globe, all on one screen ---------- */
@media (min-width:900px){
  body.kx-drawing .kx-hero-grid{align-items:start}
  body.kx-drawing{padding-bottom:0}          /* 🛠️ the mobile scroll hack is not needed here */
  .drawp{margin-top:0}
}

/* keep the whole thing inside the viewport at every width */
html,body{overflow-x:hidden}

/* ==========================================================================
   css/v4/kx_layout.css
   ========================================================================== */
/* kx_layout.css v4 — desktop layouts.                                       🛠️ P1-19
 *
 * Two jobs:
 *   1. the hub globe must never spill out of its grid column (at 1920 it was 820px wide in
 *      an 812px column, which is why it intersected the search column);
 *   2. the kanji page becomes two columns on a wide screen instead of a phone-width strip.
 *
 * MOBILE IS UNTOUCHED. Everything below lives inside a min-width query, and the desktop
 * arrangement is done with GRID PLACEMENT, not by reordering the DOM — so the reading order
 * a phone and a screen reader get is exactly the order that shipped.
 */

/* ---------- 1. hub: keep the globe inside its column ---------- */
.kx-globe-box{max-width:100%}
@media (min-width:900px){
  .kx-globe-col{min-width:0}          /* a grid child will not shrink below content without this */
}

/* ---------- 2. kanji page: two columns ---------- */
@media (min-width:1000px){
  main.wrap{max-width:1240px}
  header.kx-nav,.kxft{max-width:1240px;margin-left:auto;margin-right:auto}

  main.wrap.kx-two{
    display:grid;
    grid-template-columns:minmax(320px,400px) minmax(0,1fr);
    column-gap:clamp(28px,3vw,56px);
    align-items:start;
  }
  /* the two rails. display:contents on mobile means these wrappers do nothing there. */
  .kx-col-l{grid-column:1;position:sticky;top:16px;align-self:start}
  .kx-col-r{grid-column:2;min-width:0}
  .kx-col-full{grid-column:1 / -1}

  /* a bigger hero now that it has room */
  .kx-col-l .stage{width:min(400px,100%)}
  .kx-col-l .hero{display:block}
  .kx-col-l .facts{margin-top:16px}

  /* the scrubber can breathe */
  .kx-scrub-stage{width:min(34vw,260px)}
}
@media (min-width:1280px){
  main.wrap{max-width:1280px}
  header.kx-nav,.kxft{max-width:1280px}
  main.wrap.kx-two{grid-template-columns:minmax(360px,420px) minmax(0,1fr)}
  .kx-col-l .stage{width:min(420px,100%)}
}
@media (min-width:1600px){
  main.wrap{max-width:1360px}
  header.kx-nav,.kxft{max-width:1360px}
}

/* results page and the friendly 404 get the same wider container */
@media (min-width:1000px){
  body.kx-results main.wrap{max-width:1240px}
}

/* mobile: the rails are not boxes, they are nothing at all */
@media (max-width:999.98px){
  .kx-col-l,.kx-col-r,.kx-col-full{display:contents}
}

/* ==========================================================================
   css/v5/kx_fix.css
   ========================================================================== */
/* kx_fix.css v5 — three measured defects from the size matrix.             🛠️ P1-19b
 * Loads after v3/v4 (versioned files are never overwritten), so it wins on order.
 */

/* 1. 320px horizontal overflow on /kanji/愛.
 *    The P1-14 furigana fallback pill was white-space:nowrap, so a long sentence reading
 *    (307px) could not wrap inside a 320px viewport. Measured: 20px of overflow. */
.kx-rdfb{white-space:normal;overflow-wrap:anywhere}

/* 2. The kanji-page container widths were written as `main.wrap`, which also matched the
 *    HUB and shrank it from 1520 to 1360 — that is why the globe column stopped at 760px
 *    and the globe never earned its large-desktop population. Scope them to the grid. */
@media (min-width:1000px){
  main.wrap:not(.kx-two){max-width:none}          /* hand the hub back to kx_hub2.css */
  main.wrap.kx-two{max-width:1240px}
}
@media (min-width:1280px){ main.wrap.kx-two{max-width:1280px} }
@media (min-width:1600px){ main.wrap.kx-two{max-width:1360px} }

/* 3. On the results page there is no hero grid, so nothing bounded the globe and it grew to
 *    1181px at 2560. Only the globe INSIDE the hero column is allowed to be large. */
.kx-globe-box{max-width:min(100%,560px)}
@media (min-width:900px){
  .kx-globe-col .kx-globe-box{max-width:min(100%,820px)}
}

/* ==========================================================================
   css/v6/kx_hero.css
   ========================================================================== */
/* kx_hero.css v6 — the hub's desktop hero really is the first screen.      🛠️ P1-19c
 *
 * The globe + search are a grid, but Kanji of the Day still came BEFORE them in the DOM, so
 * on a 1080-tall screen the hero started ~470px down and the globe ran off the bottom.
 * Order is changed visually only, with flex order — the DOM (and the reading order a phone
 * and a screen reader get) is untouched, and :has() scopes it to the page that has a hero.
 */
@media (min-width:900px){
  main.wrap:has(.kx-hero-grid){display:flex;flex-direction:column}
  main.wrap:has(.kx-hero-grid) > h1.hub-h1   {order:1}
  main.wrap:has(.kx-hero-grid) > p.sub       {order:2}
  main.wrap:has(.kx-hero-grid) > .kx-hero-grid{order:3}
  main.wrap:has(.kx-hero-grid) > .kotd       {order:4; margin-top:8px}
  main.wrap:has(.kx-hero-grid) > .kx-legend  {order:5}

  /* the hero now owns the first screen */
  .kx-hero-grid{min-height:calc(100vh - 150px)}
}

/* ==========================================================================
   css/v8/kx_order.css
   ========================================================================== */
/* kx_order.css v8 — hub desktop order.                                     🛠️ P1-20B
 *
 * BUG introduced by P1-19c: making <main> a flex column and giving order:1..4 to the H1,
 * sub, hero grid and KOTD left every OTHER child at the default order:0 — so the EDRDG
 * footer sorted to the very top of the page on desktop. Anything unnamed now goes last.
 *
 * SECTION B: the search block sits at the TOP of the right column, level with the top of
 * the globe, so it is the first thing in reach on a wide screen. Mobile is untouched.
 */
@media (min-width:900px){
  main.wrap:has(.kx-hero-grid) > *            {order:6}   /* default: after the named ones */
  main.wrap:has(.kx-hero-grid) > h1.hub-h1    {order:1}
  main.wrap:has(.kx-hero-grid) > p.sub        {order:2}
  main.wrap:has(.kx-hero-grid) > .kx-hero-grid{order:3}
  main.wrap:has(.kx-hero-grid) > .kotd        {order:4;margin-top:8px}
  main.wrap:has(.kx-hero-grid) > .kx-legend   {order:5}
  main.wrap:has(.kx-hero-grid) > footer.kxft  {order:9}   /* 🛠️ was sorting to the top */

  /* B — search at the top of its column, level with the globe */
  .kx-hero-grid{align-items:start}
  .kx-search-col{padding-top:4px}
}

/* ==========================================================================
   kanji_hub.php inline <style> (lines 134-273)
   ========================================================================== */
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
[hidden]{display:none!important}
:root{--bg:#faf5ef;--surface:#fdf9f4;--subtle:#f3eadf;--text:#241d1b;--text-2:#3a302d;--muted:#5b514d;
 --border:rgba(237,31,36,.14);--border-strong:rgba(237,31,36,.26);--red:#ed1f24;--gold:#c9a227;
 --font-display:'Shippori Mincho','Hiragino Mincho ProN','Yu Mincho',serif;
 --font-body:'Hiragino Sans','Hiragino Kaku Gothic ProN','Noto Sans JP','Noto Sans CJK JP','Yu Gothic UI','Yu Gothic',Meiryo,system-ui,-apple-system,'Segoe UI',sans-serif;   /* 🛠️ P1-20C */
 --font-np:'Noto Sans Devanagari','Hiragino Sans','Hiragino Kaku Gothic ProN','Noto Sans JP','Noto Sans CJK JP','Yu Gothic UI','Yu Gothic',Meiryo,system-ui,-apple-system,'Segoe UI',sans-serif}   /* 🛠️ P1-20C */
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--bg:#151011;--surface:#1e1719;--subtle:#2a2124;
 --text:#f2ebe6;--text-2:#f2ebe6;--muted:#b5a8a3;--border:rgba(255,95,99,.18);--border-strong:rgba(255,95,99,.30);
 --red:#ff5f63;--gold:#d9b64a}}
:root[data-theme="dark"],html.dark{--bg:#151011;--surface:#1e1719;--subtle:#2a2124;--text:#f2ebe6;--text-2:#f2ebe6;
 --muted:#b5a8a3;--border:rgba(255,95,99,.18);--border-strong:rgba(255,95,99,.30);--red:#ff5f63;--gold:#d9b64a}
body{background:var(--bg);color:var(--text);font:16px/1.6 var(--font-body);   /* 🛠️ P1-6e */
 padding-bottom:calc(40px + env(safe-area-inset-bottom))}
.wrap{max-width:720px;margin:0 auto;padding:0 16px}
a{color:inherit}
.kx-nav{display:flex;align-items:center;justify-content:space-between;gap:12px;max-width:720px;margin:0 auto;
 padding:12px 16px;border-bottom:1px solid var(--border)}
.kx-brand{display:flex;align-items:center;gap:8px;font-weight:700;text-decoration:none;font-size:15px}
.kx-nav nav{display:flex;gap:14px;font-size:14px}.kx-nav nav a{text-decoration:none;color:var(--muted)}
.darkbar{max-width:720px;margin:12px auto 0;padding:9px 12px;border:1px dashed var(--border-strong);
 border-radius:10px;background:var(--subtle);font-size:13px;color:var(--muted)}
h1{font-size:23px;margin:26px 0 4px;letter-spacing:.01em;font-family:var(--font-display);font-weight:700}
.sub{color:var(--muted);font-size:14px;margin-bottom:16px}
/* the box */
.sbox{position:relative}
.sbox form{display:flex;gap:8px}
.sbox input{flex:1 1 auto;min-width:0;font-size:18px;padding:14px 16px;border-radius:12px;
 border:1px solid var(--border-strong);background:var(--surface);color:var(--text)}
.sbox input:focus{outline:2px solid var(--red);outline-offset:1px}
.sbox button{flex:0 0 auto;padding:0 20px;border:0;border-radius:12px;background:var(--red);color:#fff;
 font-size:15px;font-weight:700;cursor:pointer}
.hint{color:var(--muted);font-size:13px;margin-top:8px}
/* 🆕 P1-12 draw pad */
/* 🛠️ P1-12b — labelled mode tabs */
.modes{display:flex;gap:8px;margin-bottom:10px}
.mode{flex:1 1 0;min-width:0;padding:9px 10px;border:1px solid var(--border-strong);border-radius:12px;
 background:var(--surface);color:var(--text-2);cursor:pointer;text-align:center;font-family:var(--font-body)}
.mode .ml{display:block;font-size:14.5px;font-weight:700}
.mode .mn{display:none;font-family:var(--font-np);font-weight:500;font-size:11.5px;color:var(--muted);margin-top:2px}
.mode.on{border-color:var(--red);background:var(--subtle);color:var(--text)}
.mode.on .mn{display:block}
.mode:focus-visible{outline:2px solid var(--red);outline-offset:2px}
/* the pad column: sized so pad + controls + chips fit one phone screen without scrolling.
   --padmax is the height left over once the controls (44) and the chip row (58) are taken,
   plus breathing room; dvh follows the browser chrome as it hides. */
.drawp{margin-top:10px;background:var(--surface);border:1px solid var(--border);border-radius:14px;
 padding:12px;scroll-margin-top:10px}
#kx-pad{display:block;width:min(88vw, 360px);aspect-ratio:1;margin:0 auto;background:var(--paper);
 border:1px solid var(--border-strong);border-radius:12px;touch-action:none;cursor:crosshair}
@supports (height:100dvh){ #kx-pad{width:min(88vw, 360px, calc(100dvh - 230px))} }
.drawctl{display:flex;gap:8px;margin-top:10px;align-items:center;flex-wrap:wrap;justify-content:center}
.mini{padding:9px 13px;border:1px solid var(--border-strong);background:transparent;color:var(--text);
 border-radius:10px;font-size:14px;cursor:pointer}
.mini:hover{background:var(--subtle)}
.rare{display:flex;gap:6px;align-items:center;font-size:12.5px;color:var(--muted)}
.cands{display:flex;flex-wrap:wrap;gap:7px;margin-top:10px;min-height:50px;justify-content:center}
.cand{display:inline-flex;align-items:center;justify-content:center;min-width:48px;height:48px;padding:0 8px;
 font-family:var(--font-display);font-size:26px;text-decoration:none;color:var(--text);
 border:1px solid var(--border-strong);border-radius:11px;background:var(--paper)}
.cand:hover,.cand:focus-visible{border-color:var(--red);outline:none}
/* 🆕 P1-12b fix — on a SHORT screen (375x667) the document simply was not tall enough to
   scroll the pad up to the top, so the chips stayed below the fold no matter what
   scrollIntoView asked for. This reserves the room to scroll into; it is empty space below
   the pad that nobody ever sees once the panel is at the top. */
body.kx-drawing{padding-bottom:70vh}
.np-h{font-family:var(--font-np);font-weight:500}
#kx-drawstat{text-align:center;margin-top:8px}
@media (min-width:620px){
  .drawp{display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:start}
  #kx-pad{width:300px;margin:0}
  .drawctl{margin-top:0;justify-content:flex-start}
  .cands{margin-top:10px;justify-content:flex-start}
  .drawp > .drawctl,.drawp > .cands,.drawp > #kx-drawstat{grid-column:2}
  #kx-pad{grid-row:1 / span 3}
  #kx-drawstat{text-align:left}
}
.hint a{color:var(--red);text-decoration:none;border-bottom:1px dotted currentColor}
/* suggestions */
.sug{position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:30;background:var(--surface);
 border:1px solid var(--border-strong);border-radius:12px;overflow:hidden;box-shadow:0 10px 30px rgba(0,0,0,.14)}
.sug b.gl{display:block;padding:7px 14px;font-size:11.5px;font-weight:700;color:var(--muted);
 background:var(--subtle);letter-spacing:.04em;text-transform:uppercase}
.sug a{display:flex;gap:10px;align-items:baseline;padding:9px 14px;text-decoration:none;border-top:1px solid var(--border)}
.sug a:first-of-type{border-top:0}
.sug a.on,.sug a:hover{background:var(--subtle)}
.sug .c{font-family:var(--font-display);font-size:24px;line-height:1.1;font-weight:500}
.sug .m{flex:1 1 auto;min-width:0;font-size:13.5px;color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sug .r{font-size:12px;color:var(--muted)}
/* results */
h2{font-size:14px;margin:26px 0 10px;color:var(--muted);letter-spacing:.04em;text-transform:uppercase;font-weight:700}
ul.res{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px}
ul.res li{background:var(--surface);border:1px solid var(--border);border-radius:12px}
ul.res a,ul.res .np-item{display:flex;gap:12px;align-items:flex-start;padding:12px;text-decoration:none}
.c{font-family:var(--font-display);font-size:34px;line-height:1;flex:0 0 auto;font-weight:500}
.meta{min-width:0}
.meta .en{font-size:14px;color:var(--text-2);overflow-wrap:anywhere}
.meta .np{font-size:13px;color:var(--muted);overflow-wrap:anywhere;font-family:var(--font-np);font-weight:500}
.meta .rd{font-size:12.5px;color:var(--muted)}
.badge{display:inline-block;background:var(--gold);color:#fff;border-radius:5px;padding:1px 7px;font-size:11.5px;font-weight:700}
.tag{font-size:11px;color:var(--muted);border:1px solid var(--border);border-radius:4px;padding:0 5px}
.nostroke-tag{opacity:.9;border-style:dashed}   /* 🛠️ P1-20D — .75 measured 3.94:1 */
.empty{background:var(--surface);border:1px solid var(--border);border-radius:12px;padding:18px;margin-top:18px}
.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.chip{border:1px solid var(--border-strong);border-radius:999px;padding:6px 13px;font-size:14px;text-decoration:none;color:var(--text-2)}
.chip:hover{background:var(--subtle)}
/* 🆕 P1-13 — Kanji of the Day. Every box that holds art has a fixed size, so nothing
   below it moves when the SVG paints (no layout shift on a slow phone). */
.kotd{border:1px solid var(--border);border-radius:16px;background:var(--surface);
 padding:14px;margin:0 0 18px}
.kotd-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:10px}
.kotd-head h2{font:700 13px/1.2 var(--font-body);letter-spacing:.09em;text-transform:uppercase;
 color:var(--muted);margin:0}
.kotd-date{font-size:12px;color:var(--muted)}
.kotd-body{display:flex;gap:14px;align-items:flex-start}
.kotd-glyph{flex:0 0 92px;width:92px;height:92px;display:block;color:var(--text);
 border:1px solid var(--border);border-radius:12px;background:var(--bg);padding:6px}
.kotd-glyph svg{display:block;width:100%;height:100%}
.kotd-fb{display:flex;width:100%;height:100%;align-items:center;justify-content:center;
 font-family:var(--font-display);font-size:54px;line-height:1}
.kotd-info{flex:1 1 auto;min-width:0}
.kotd-lvl{display:inline-block;font:700 11px/1 var(--font-body);letter-spacing:.06em;
 padding:4px 8px;border-radius:999px;background:var(--subtle);color:var(--text-2);
 border:1px solid var(--border);margin-bottom:6px}
.kotd-rd{font-size:13px;color:var(--muted);margin:2px 0}
.kotd-en{font-size:15px;font-weight:700;margin:3px 0;overflow-wrap:anywhere}
.kotd-np{font-family:var(--font-np);font-size:14px;color:var(--text-2);margin:2px 0;overflow-wrap:anywhere}
.kotd-word{font-size:13.5px;color:var(--muted);margin-top:7px;overflow-wrap:anywhere}
.kotd-word b{color:var(--text-2);font-weight:700}
.kotd-word ruby rt{font-size:.55em;color:var(--muted)}
.kx-rdfb{font-size:.82em;color:var(--muted);white-space:nowrap}   /* 🆕 P1-14 */
.kotd-foot{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:12px}
.kotd-cta{flex:0 0 auto;background:var(--red);color:#fff;text-decoration:none;font-weight:700;
 font-size:14px;padding:9px 16px;border-radius:11px}
.kotd-share{flex:0 0 auto;border:1px solid var(--border-strong);background:transparent;
 color:var(--text-2);font:inherit;font-size:13.5px;padding:8px 14px;border-radius:11px;cursor:pointer}
.kotd-share:hover{border-color:var(--red);color:var(--red)}
.kotd-tmr{margin-left:auto;font-size:12px;color:var(--muted)}
@media (max-width:420px){.kotd-glyph{flex-basis:76px;width:76px;height:76px}
 .kotd-tmr{margin-left:0;width:100%}}

/* ==========================================================================
   css/v9/kx_a11y.css
   ========================================================================== */
/* kx_a11y.css v9 — DESIGN PASS 2c section D: contrast on the kanji engine.  🆕 P1-20D
 *
 * #ed1f24 STAYS the display red. Hero strokes, the globe, the hanko frame and any
 * heading >=24px (or >=18.66px bold) only need 3:1, and it clears that on every paper
 * tone (4.29 / 4.02 / 3.91 / 3.34). What fails 4.5:1 is small red text and the
 * white-on-red controls. Measured on live at 390px, 2026-09-26:
 *
 *   .hint a, .kx-w .hk             #ed1f24 on #faf5ef   4.02
 *   .badge.kx-hanko                #ed1f24 on #f8dbd7   3.34  (16px/700 is NOT large text)
 *   .btn / .sbox button /          #fff    on #ed1f24   4.35  light
 *     .kotd-cta / selected tab     #fff    on #ff5f63   2.97  dark
 *   .badge  (JLPT gold seal)       #fff    on #c9a227   2.42 light / 1.96 dark
 *   .steps .no                     rgba(36,29,27,.45)   2.82
 *   .stage .wm                     rgba(36,29,27,.28)   1.82
 *
 * --kx-red-text #c2181c is the LIGHTEST red that clears 4.5 on all four paper tones
 * (6.01 / 5.63 / 5.48 / 4.68) and gives a white label 6.10 on the fill.
 *
 * DARK KEEPS #ff5f63. Darkening the fill there would drop it to 3.03 against the
 * #151011 ground, under the 3:1 that 1.4.11 wants of a control, so in dark the LABEL
 * goes dark instead: #241d1b on #ff5f63 = 5.58, fill vs ground 6.22. Dark red TEXT is
 * left exactly as it is — it measures passing today and it sits on other surfaces.
 *
 * Linked AFTER each page's own inline <style>, so equal-specificity rules win on order.
 * .btn.ghost (0,2,0) still out-specifies the .btn rule below and keeps its own colours.
 */
:root{--kx-red-text:#c2181c;--kx-btn-bg:#c2181c;--kx-btn-fg:#fff}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){--kx-red-text:#ff5f63;--kx-btn-bg:#ff5f63;--kx-btn-fg:#241d1b}
}
:root[data-theme="dark"],html.dark{--kx-red-text:#ff5f63;--kx-btn-bg:#ff5f63;--kx-btn-fg:#241d1b}

/* ---- small red text ---- */
.hint a{color:var(--kx-red-text)}
.kx-w .hk,.kx-wdetail .dj .hk{color:var(--kx-red-text)}

/* ---- white-on-red controls ---- */
.btn,.sbox button,.kotd-cta,.kx-wtabs button[aria-selected=true]{
  background:var(--kx-btn-bg);color:var(--kx-btn-fg)}

/* ---- JLPT gold seal: keep the gold, ink the label (2.42 / 1.96 -> 6.85 / 8.48) ---- */
.badge{color:#241d1b}
/* the hanko seal is the red badge, not the gold one — (0,2,0) keeps it out of the rule above */
.badge.kx-hanko{color:var(--kx-red-text);border-color:var(--kx-red-text)}

/* ---- muted ink ON PAPER. --paper is cream in BOTH themes, so this is one literal in
        both, exactly as kx_paper.css does it (.45/.28 -> .62 = 4.67 light / 4.51 dark) ---- */
.steps .no,.steps.kx-thumbs .no{color:rgba(36,29,27,.62)}
.stage .wm{color:rgba(36,29,27,.62)}

/* ==========================================================================
   🆕 P1-21 — css/v11 = the v10 bundle above, byte-for-byte, + THIS layer.
   Design pass 2d (Umesh, real Mac): a genuinely separate DESKTOP hub.
   Everything below is scoped to body.kxn (the next UI) and, for layout, to ≥1000px —
   so a phone renders exactly what v10 renders.
   ========================================================================== */

/* admin preview bar (only ever emitted to an admin in preview) */
.kx-previewbar{position:fixed;left:12px;bottom:12px;z-index:70;padding:7px 12px;border-radius:10px;
  background:#241d1b;color:#fff;font:600 12.5px/1.4 system-ui,sans-serif;box-shadow:0 4px 18px rgba(0,0,0,.25)}
.kx-previewbar a{color:#ffb3b5;margin-left:6px}

@media (min-width:1000px){
  body.kxn{
    --kxn-hdr:54px;                 /* header_lean height (measured) */
    --kxn-head:100px;               /* the title block below, pinned so the maths is exact */
    --kxn-hero:calc(100dvh - var(--kxn-hdr) - var(--kxn-head) - 20px);
    padding-bottom:0;
  }
  body.kxn header.kx-nav{max-width:1440px}
  body.kxn main.wrap{
    max-width:1440px;padding:0 24px;
    display:grid;
    grid-template-columns:minmax(0,1fr) clamp(400px,34vw,500px);
    grid-template-rows:auto auto var(--kxn-hero);
    column-gap:28px;
  }
  body.kxn main.wrap > *{grid-column:1 / -1}                  /* anything unnamed: full width, below */
  body.kxn .kx-hero-grid{display:contents}

  /* the title block: right column, pinned heights */
  body.kxn main.wrap > h1.hub-h1{grid-column:2;grid-row:1;font-size:40px;line-height:48px;height:48px;
    margin:16px 0 0;white-space:nowrap;overflow:hidden}
  body.kxn main.wrap > p.sub{grid-column:2;grid-row:2;line-height:22px;height:22px;margin:2px 0 12px;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

  /* LEFT: the globe. Its area is fixed to the first screen, so it never moves or resizes
     while you search, draw or read results. */
  body.kxn .kx-globe-col{grid-column:1;grid-row:1 / span 3;min-width:0;align-self:stretch;
    display:flex;align-items:center;justify-content:center;padding:10px 0}
  body.kxn .kx-globe-col .kx-globe-box{
    width:min(100%, calc(var(--kxn-hero) + var(--kxn-head) - 10px));
    max-width:820px;margin:0;aspect-ratio:1}
  body.kxn main.wrap > .kx-legend{grid-column:1;grid-row:3;align-self:end;justify-self:center;
    margin:0 0 6px;position:relative;z-index:2;pointer-events:none}

  /* RIGHT: one panel, its own scroll area. Top of the panel = top of the globe area. */
  body.kxn .kx-search-col{grid-column:2;grid-row:3;min-width:0;height:var(--kxn-hero);
    overflow-y:auto;overscroll-behavior:contain;scrollbar-gutter:stable;
    padding:0 6px 16px 2px;align-self:start}
  body.kxn .kxn-bar{position:sticky;top:0;z-index:5;background:var(--bg);padding:2px 0 10px}
  body.kxn .kxn-bar .sbox input{width:100%}                   /* the box never changes width */
  body.kxn .kx-search-col .hint{margin-top:0}

  /* results live IN the panel: two columns of cards, not a full-width grid */
  body.kxn .kx-search-col h2{font-size:15px;margin:18px 0 8px;font-family:var(--font-display)}
  body.kxn .kx-search-col ul.res{grid-template-columns:repeat(auto-fill,minmax(190px,1fr))}
  body.kxn .kx-search-col .empty{margin-top:18px}

  /* draw mode: the pad sits in the panel with its candidates right under it — the globe
     stays where it is and the page does not scroll */
  body.kxn.kx-drawing{padding-bottom:0}
  body.kxn .kx-search-col .drawp{display:block;margin-top:0}
  body.kxn .kx-search-col #kx-pad{grid-row:auto;margin:0 auto;
    width:min(100%, 420px, calc(var(--kxn-hero) - 250px))}
  body.kxn .kx-search-col .drawctl{justify-content:center;margin-top:10px}
  body.kxn .kx-search-col .cands{justify-content:center}
  body.kxn .kx-search-col #kx-drawstat{text-align:center}

  /* Kanji of the Day: below the fold, full width, unchanged */
  body.kxn main.wrap > .kotd{grid-row:4;margin-top:28px}
  body.kxn main.wrap > footer.kxft{grid-row:5}
}

/* ==========================================================================
   🆕 P1-31 — css/v13 = the v11 bundle above, byte-for-byte, + THIS layer.
   The "Kanji Universe" background (js/v14/kx_universe.js): one fixed canvas behind the hub.
   ========================================================================== */
.kx-bg{position:fixed;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;display:block}
body.kxu header.kx-nav,body.kxu main.wrap{position:relative;z-index:1}   /* all content sits above the background */
