  :root{
    --bg:#0b0e1a;
    --bg-elevated:#101528;
    --surface:#161d34;
    --surface-2:#1b2340;
    --surface-hover:#212a4c;
    --border:#2a3358;
    --text:#e9e8f4;
    --text-dim:#9aa1c4;
    --text-faint:#848cb2;
    --accent:#e3b341;
    --accent-dim:#8a712c;
    --accent-2:#7c9eff;
    --accent-purple:#a97cf0;
    --danger:#ef6a6a;
    --radius:14px;
    --radius-sm:8px;
    --brand-grad:linear-gradient(120deg, var(--accent), #c9a6f5);
  }
  *{box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
  html,body{margin:0;padding:0;}
  body{
    background:
      radial-gradient(900px 620px at 12% -8%, #17204a 0%, transparent 60%),
      radial-gradient(700px 500px at 100% 0%, rgba(169,124,240,.12) 0%, transparent 55%),
      var(--bg) fixed;
    color:var(--text);
    font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
    font-size:15px;
    line-height:1.5;
    -webkit-font-smoothing:antialiased;
    min-height:100vh;
  }
  button, select, input{ font-family:inherit; }
  #app{max-width:640px;margin:0 auto;min-height:100vh;display:flex;flex-direction:column;animation:fadeIn .35s ease;}
  @keyframes fadeIn{from{opacity:0}to{opacity:1}}

  /* ---------- header ---------- */
  header.topbar{
    position:sticky;top:0;z-index:20;
    background:
      radial-gradient(360px 140px at 15% 0%, rgba(169,124,240,.18), transparent 70%),
      linear-gradient(180deg, rgba(11,14,26,.97), rgba(11,14,26,.88));
    backdrop-filter:blur(6px);
    border-bottom:1px solid var(--border);
    padding:10px 14px 0 14px;
  }
  .topbar-row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:8px;}
  .brand{
    font-family:'Exo 2',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
    font-size:23px;font-weight:300;margin:0;letter-spacing:.5px;cursor:pointer;
  }
  .brand .t-cosmic{color:#f3f2fb;}
  .brand .t-archive{color:var(--accent-purple);text-shadow:0 0 16px rgba(169,124,240,.4);}
  /* Was a dotted underline by default (an unstyled affordance hint left
     over from an earlier pass) — with no matching "tap to expand" cue
     nearby it just read as a stray line under a header nobody asked to
     interact with. Plain caption text instead. */
  .brand small{display:block;font-size:10.5px;font-weight:500;color:var(--text-faint);
    background:none;-webkit-text-fill-color:var(--text-faint);letter-spacing:.4px;margin-top:1px;}
  .topbar-actions{display:flex;align-items:center;gap:6px;flex-shrink:0;}
  .iconbtn{
    width:36px;height:36px;border-radius:10px;border:1px solid var(--border);
    background:var(--surface);color:var(--text-dim);font-size:16px;
    display:flex;align-items:center;justify-content:center;cursor:pointer;
    transition:transform .15s ease, color .15s ease, border-color .15s ease;
  }
  .iconbtn:active{transform:scale(.92);}
  .iconbtn.spinning{animation:spin .7s linear infinite;color:var(--accent-2);}
  @keyframes spin{to{transform:rotate(360deg);}}
  .searchRow{margin-bottom:8px;}
  .searchRow input{
    width:100%;padding:10px 12px;border-radius:11px;border:1px solid var(--border);
    background:var(--surface);color:var(--text);font-size:14.5px;outline:none;
    transition:border-color .15s ease;
  }
  .searchRow input:focus{border-color:var(--accent-2);}
  .searchRow input::placeholder{color:var(--text-faint);}

  nav.tabs{
    position:relative;display:flex;gap:2px;padding:4px;margin-bottom:10px;
    background:var(--surface);border:1px solid var(--border);border-radius:14px;
  }
  .tab-indicator{
    position:absolute;top:4px;bottom:4px;left:0;width:0;
    background:var(--brand-grad);
    border-radius:11px;z-index:0;
  }
  .tab-indicator.ready{
    transition:transform .32s cubic-bezier(.22,1,.36,1), width .32s cubic-bezier(.22,1,.36,1);
  }
  .tab{
    position:relative;z-index:1;
    flex:1 1 0;min-width:0;padding:8px 2px;border-radius:11px;border:none;
    background:transparent;color:var(--text-dim);font-size:10px;line-height:1.2;font-weight:600;
    cursor:pointer;white-space:normal;text-align:center;overflow-wrap:anywhere;
    transition:color .2s ease;
  }
  .tab.active{color:#241c04;}

  /* ---------- content / grid ---------- */
  main#content{flex:1;padding:14px;}

  #filterBar{margin-bottom:12px;}
  .filter-toggle{
    display:inline-flex;align-items:center;gap:6px;
    background:var(--surface);border:1px solid var(--border);border-radius:999px;
    color:var(--text-dim);font-size:12.5px;font-weight:600;cursor:pointer;padding:6px 12px;
    transition:border-color .15s ease, color .15s ease;
  }
  .filter-toggle:hover{color:var(--text);border-color:var(--accent-dim);}
  #filterCount{
    background:var(--accent-2);color:#0b0e1a;font-size:10px;font-weight:800;
    border-radius:999px;padding:1px 6px;min-width:14px;text-align:center;display:none;
  }
  #filterCount.show{display:inline-block;}
  /* The height runs through a 0fr -> 1fr grid row, so it follows the panel's real
     height from the first frame to the last (a max-height guess finishes early and
     reads as a snap). */
  #filterCaret{display:inline-block;font-size:11px;transition:transform .35s cubic-bezier(.22,.8,.26,1);}
  .filter-toggle.open #filterCaret{transform:rotate(180deg);}
  .filter-panel{
    display:grid;grid-template-rows:0fr;opacity:0;visibility:hidden;margin-top:0;
    transition:grid-template-rows .42s cubic-bezier(.22,.8,.26,1), opacity .3s ease,
               margin-top .42s cubic-bezier(.22,.8,.26,1), visibility 0s linear .42s;
  }
  .filter-panel.open{
    grid-template-rows:1fr;opacity:1;visibility:visible;margin-top:10px;transition-delay:0s;
  }
  .filter-panel-inner{
    min-height:0;overflow:hidden;transform:translateY(-8px);
    transition:transform .42s cubic-bezier(.22,.8,.26,1);
  }
  .filter-panel.open .filter-panel-inner{transform:none;}
  @media (prefers-reduced-motion:reduce){
    .filter-panel,.filter-panel-inner,#filterCaret{transition-duration:.01s;transition-delay:0s;}
  }
  .filter-group{margin-bottom:10px;}
  .filter-group.hidden{display:none;}
  .filter-label{font-size:11px;letter-spacing:.5px;color:var(--text-faint);margin-bottom:6px;font-weight:700;}
  .filter-chips{display:flex;flex-wrap:wrap;gap:6px;}
  .fchip{
    display:inline-flex;align-items:center;padding:6px 12px;border-radius:999px;
    border:1px solid var(--border);background:var(--surface);color:var(--text-dim);
    font-size:12px;font-weight:600;cursor:pointer;transition:border-color .15s ease, color .15s ease;
  }
  .fchip.active{background:var(--accent-2);border-color:var(--accent-2);color:#0b0e1a;}
  .grid{
    display:grid;grid-template-columns:repeat(auto-fill,minmax(94px,1fr));gap:10px;
  }
  .card{
    background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);
    padding:8px;cursor:pointer;text-align:center;position:relative;
    transition:border-color .15s ease, transform .12s ease, box-shadow .15s ease;
  }
  .card:active{transform:scale(.96);}
  .card.r5{border-color:rgba(227,179,65,.4);box-shadow:inset 0 0 0 1px rgba(227,179,65,.35);}
  .card.r5 .thumb{background:radial-gradient(circle at 50% 30%, rgba(227,179,65,.28), rgba(227,179,65,.04) 70%);}
  .card.r4{border-color:rgba(124,158,255,.28);}
  .card.r4 .thumb{background:radial-gradient(circle at 50% 30%, rgba(124,158,255,.20), rgba(124,158,255,.03) 70%);}
  .thumb{
    width:100%;aspect-ratio:1/1;border-radius:10px;margin-bottom:6px;
    display:flex;align-items:center;justify-content:center;overflow:hidden;
    background:var(--surface-2);
  }
  .thumb img{width:90%;height:90%;object-fit:contain;}
  .thumb img.img-error{opacity:0;}
  .card-name{font-size:11.5px;font-weight:600;color:var(--text);line-height:1.25;
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
  .card-stars{font-size:9.5px;color:var(--accent);margin-top:2px;letter-spacing:1px;}
  .card-stars + .card-stars{font-size:10.5px;}
  .beta-chip{
    position:absolute;top:4px;right:4px;background:var(--accent-2);color:#0b0e1a;
    font-size:8px;font-weight:800;padding:1.5px 4px;border-radius:5px;letter-spacing:.3px;
  }
  .element-dot{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:3px;vertical-align:middle;}
  .element-icon{display:inline-flex;align-items:center;justify-content:center;margin-right:4px;vertical-align:middle;flex-shrink:0;}
  .element-icon svg, .element-icon img{width:15px;height:15px;display:block;object-fit:contain;}
  .element-icon.lg svg, .element-icon.lg img{width:20px;height:20px;}

  @media (hover:hover) and (pointer:fine){
    .card:hover{border-color:var(--accent-dim);transform:translateY(-2px);box-shadow:0 10px 24px -14px rgba(0,0,0,.6);}
    .card.r5:hover{box-shadow:inset 0 0 0 1px rgba(227,179,65,.5), 0 10px 26px -12px rgba(227,179,65,.25);}
  }

  .state-msg{
    text-align:center;color:var(--text-dim);padding:40px 20px;font-size:14px;
  }
  .state-msg .big{font-size:28px;display:block;margin-bottom:8px;}
  .retry-btn{
    margin-top:12px;padding:9px 18px;border-radius:10px;border:1px solid var(--accent-2);
    background:transparent;color:var(--accent-2);font-weight:600;font-size:13.5px;cursor:pointer;
  }
  .spinner{
    width:26px;height:26px;border-radius:50%;border:3px solid var(--border);
    border-top-color:var(--accent-2);animation:spin .7s linear infinite;margin:30px auto;
  }

  footer.foot{padding:18px 14px 26px;text-align:center;color:var(--text-faint);font-size:11px;}
  footer.foot a{color:var(--text-faint);}

  /* ---------- overlay / detail ---------- */
  .overlay{
    position:fixed;inset:0;background:var(--bg);z-index:50;
    display:flex;flex-direction:column;transform:translateY(8px);opacity:0;
    transition:transform .18s ease, opacity .18s ease;pointer-events:none;
  }
  .overlay.show{transform:translateY(0);opacity:1;pointer-events:auto;}
  .overlay-inner{max-width:640px;width:100%;margin:0 auto;height:100%;display:flex;flex-direction:column;overflow:hidden;}
  .overlay-head{
    display:flex;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--border);
    background:var(--bg-elevated);flex-shrink:0;
  }
  .backBtn{
    background:var(--surface);border:1px solid var(--border);color:var(--text);
    border-radius:10px;padding:8px 12px;font-size:13.5px;font-weight:600;cursor:pointer;flex-shrink:0;
  }
  .overlay-head .oh-title{font-size:14px;font-weight:700;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  #detailBody{flex:1;overflow-y:auto;padding:0 0 30px;}

  .dh{padding:20px 16px 14px;display:flex;gap:14px;align-items:center;border-bottom:1px solid var(--border);}
  .dh-thumb{width:84px;height:84px;border-radius:16px;background:var(--surface-2);flex-shrink:0;
    display:flex;align-items:center;justify-content:center;overflow:hidden;}
  .dh-thumb img{width:92%;height:92%;object-fit:contain;}
  .dh-thumb img.thumb-top{object-fit:cover;object-position:center 15%;}
  .dh-name{font-size:19px;font-weight:800;margin:0 0 4px;}
  .dh-stars{color:var(--accent);font-size:13px;letter-spacing:1.5px;margin-bottom:6px;}
  .badges{display:flex;flex-wrap:wrap;gap:5px;}
  .badge{
    font-size:11px;font-weight:600;padding:3px 9px;border-radius:999px;
    background:var(--surface-2);border:1px solid var(--border);color:var(--text-dim);
    display:inline-flex;align-items:center;
  }

  .splash-wrap{position:relative;margin:14px 16px 0;border-radius:16px;overflow:hidden;
    background:var(--surface-2);line-height:0;
    box-shadow:0 0 0 1px var(--ring,transparent), 0 18px 36px -16px var(--ring,transparent);}
  .splash-wrap img{width:100%;max-height:360px;object-fit:cover;display:block;}
  .splash-fade{position:absolute;left:0;right:0;bottom:0;height:64px;
    background:linear-gradient(180deg, rgba(11,14,26,0), var(--bg) 94%);pointer-events:none;}
  .splash-top{position:absolute;left:0;right:0;top:0;height:70px;
    background:linear-gradient(180deg, var(--ring,transparent), rgba(11,14,26,0));
    opacity:.35;pointer-events:none;}

  /* padding matches .dh/.splash-wrap's 16px side inset (was 14px) so this
     row's edges line up with the image and header above it instead of
     sitting 2px off; align-items+white-space keep every pill the same
     single-line height so the row can't go lopsided if a label is ever
     longer than the rest. */
  /* padding matches .dh/.splash-wrap's 16px side inset (was 14px) so this
     row's edges line up with the image and header above it instead of
     sitting 2px off; align-items+white-space keep every pill the same
     single-line height so the row can't go lopsided if a label is ever
     longer than the rest. mask-image fades the last ~28px to transparent
     whenever the row overflows, so a tab cut off mid-word at the viewport
     edge (e.g. "Messages") reads as "more to scroll" instead of looking
     like it was clipped by a layout bug. */
  .subtabs{display:flex;align-items:center;gap:6px;padding:22px 16px 12px;overflow-x:auto;scrollbar-width:none;
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);
    mask-image:linear-gradient(90deg,#000 calc(100% - 28px),transparent);}
  .subtabs::-webkit-scrollbar{display:none;}
  .subtab{
    flex:0 0 auto;padding:7px 13px;border-radius:999px;font-size:12.5px;font-weight:600;
    white-space:nowrap;
    border:1px solid var(--border);background:transparent;color:var(--text-dim);cursor:pointer;
    transition:border-color .15s ease, color .15s ease, background .15s ease;
  }
  .subtab.active{background:var(--surface-2);color:var(--accent-2);border-color:var(--accent-2);}

  .sec{margin:12px 14px;padding:14px 16px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);}
  /* No text-transform:uppercase: this page runs lang="tr", and the browser's
     Turkish uppercasing rule turns a lowercase "i" into "İ" — so CSS-only
     capitalization was rendering these English labels as "PROFİLE",
     "VOİCE ACTORS" etc. Written in real title case instead, so the label is
     correct wherever it's read from. A small gradient tick before the label
     (the same two-tone gradient as the tab indicator/brand wordmark) gives
     every section a consistent, branded starting point instead of the
     label just floating at the card's top edge. */
  .sec h3{font-size:12.5px;letter-spacing:.6px;color:var(--text-faint);
    margin:0 0 10px;font-weight:700;display:flex;align-items:center;gap:7px;}
  .sec h3::before{content:'';width:3px;height:12px;border-radius:2px;background:var(--brand-grad);flex-shrink:0;}
  .lore{font-size:13.5px;color:var(--text-dim);white-space:pre-line;}
  .chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px;}
  .chips.chips-full{flex-direction:column;}
  .chip{font-size:11.5px;background:var(--surface);border:1px solid var(--border);
    color:var(--text-dim);padding:4px 9px;border-radius:8px;}
  .chips-full .chip{width:100%;box-sizing:border-box;}

  .memo-header{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);
    padding:12px;margin-bottom:10px;}
  .dyn-val{color:var(--accent);font-weight:700;}
  .head-actions{display:flex;align-items:center;gap:8px;margin-left:auto;flex-shrink:0;}
  .lvl-ctrl{display:flex;align-items:center;gap:8px;flex-shrink:0;}
  .lvl-btn{width:24px;height:24px;border-radius:7px;border:1px solid var(--border);background:var(--surface-2);
    color:var(--text);font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;}
  .lvl-val{font-size:12px;color:var(--accent-2);font-weight:700;min-width:34px;text-align:center;}
  /* A level only reachable with a specific Eidolon (e.g. Memosprite Skill's
     Lv.7, one past its Lv.6 base) — same gold-to-purple gradient as the tab
     indicator, so "needs Eidolon" reads as a distinct, branded state rather
     than a color picked just for this one label. */
  .lvl-val.eidolon-lvl{
    color:var(--accent-2);
    background:linear-gradient(120deg, var(--accent), #c9a6f5);
    -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
  }
  .copy-btn{width:28px;height:28px;border-radius:8px;border:1px solid var(--border);background:var(--surface-2);
    color:var(--text-dim);display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;
    transition:color .15s ease, border-color .15s ease;}
  .copy-btn svg{width:14px;height:14px;}
  .copy-btn:active{transform:scale(.9);}
  .copy-btn:hover{color:var(--accent-2);border-color:var(--accent-2);}

  .trace-row{display:flex;gap:10px;align-items:flex-start;padding:9px 0;border-bottom:1px solid var(--border);}
  .trace-row:last-child{border-bottom:none;}
  .trace-icon{width:32px;height:32px;border-radius:8px;background:var(--surface-2);flex-shrink:0;
    display:flex;align-items:center;justify-content:center;overflow:hidden;}
  .trace-icon img{width:78%;height:78%;object-fit:contain;}
  .trace-name{font-size:13px;font-weight:700;}
  .trace-desc{font-size:12px;color:var(--text-dim);margin-top:2px;}
  .trace-lock{font-size:10.5px;color:var(--text-faint);margin-top:2px;}
  .stat-tags{display:flex;flex-wrap:wrap;gap:5px;margin-top:4px;}
  .stat-tag{font-size:10.5px;background:var(--surface-2);color:var(--accent-2);padding:2px 7px;border-radius:6px;}

  .eido-card{display:flex;gap:10px;padding:12px 0;border-bottom:1px solid var(--border);}
  .eido-card:last-child{border-bottom:none;}
  .eido-rank{width:30px;height:30px;border-radius:50%;background:var(--surface-2);border:1px solid var(--accent-dim);
    color:var(--accent);font-weight:800;font-size:13px;display:flex;align-items:center;justify-content:center;flex-shrink:0;}
  .eido-icon{width:42px;height:42px;border-radius:10px;background:var(--surface-2);border:1px solid var(--border);flex-shrink:0;
    display:flex;align-items:center;justify-content:center;overflow:hidden;}
  .eido-icon img{width:82%;height:82%;object-fit:contain;}
  .eido-body{flex:1;min-width:0;}

  table.stat-table{width:100%;border-collapse:collapse;font-size:11.5px;margin-top:4px;}
  table.stat-table th,table.stat-table td{padding:6px 7px;text-align:right;border-bottom:1px solid var(--border);white-space:nowrap;}
  table.stat-table th:first-child,table.stat-table td:first-child{text-align:left;color:var(--text-faint);}
  .stat-table-wrap{overflow-x:auto;}
  table.stat-table th{color:var(--text-faint);font-weight:600;}

  .mat-list{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px;}
  .mat-pill{display:flex;align-items:center;gap:6px;background:var(--surface);border:1px solid var(--border);
    border-radius:10px;padding:5px 9px 5px 5px;}
  .mat-pill img{width:26px;height:26px;object-fit:contain;border-radius:6px;background:var(--surface-2);}
  .mat-pill span{font-size:11.5px;color:var(--text-dim);}
  .mat-pill b{color:var(--accent);}

  /* From/To range control shared by every materials calculator (Ascension
     Materials, and each main skill's own leveling cost below) — two
     [-][N][+] steppers either side of an arrow. */
  .range-ctrl{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:6px 0 2px;}
  .range-unit-label{font-size:11.5px;color:var(--text-faint);font-weight:600;}
  .range-arrow{color:var(--text-faint);font-size:13px;}
  /* A skill's own materials calculator, nested inside its trace-row below
     the description. Wrapped in <details> (see skillRowHTML) so it starts
     collapsed — it used to live on its own separate "Materials" tab, split
     from the description it's the cost of; folding it back into the same
     row, just closed by default, keeps them one tap apart either way
     without the Talent tab turning back into a wall of text. */
  .mat-block{margin-top:10px;background:var(--surface-2);border:1px solid var(--border);border-radius:10px;overflow:hidden;}
  .mat-block summary{display:flex;align-items:center;gap:6px;padding:10px 12px;cursor:pointer;user-select:none;
    font-size:11.5px;color:var(--text-faint);font-weight:600;letter-spacing:.3px;}
  .mat-block summary::-webkit-details-marker{display:none;}
  .mat-block summary::before{content:'▸';font-size:9px;color:var(--text-faint);transition:transform .15s ease;}
  .mat-block[open] summary::before{transform:rotate(90deg);}
  .mat-block[open] summary{border-bottom:1px solid var(--border);}
  .mat-block-body{padding:10px 12px 12px;}
  /* Delta row: the calculator's actual answer (what the current From->To
     span still needs), so its label reads a size up from — and brighter
     than — Total's underneath it. It used to have no label at all, which
     left it looking like an inert duplicate of Total rather than a figure
     that responds to the range control above it. */
  .mat-delta-label{display:block;font-size:12px;color:var(--text-dim);font-weight:700;margin-bottom:2px;}
  /* Total row: a lighter-weight echo of .mat-list below the delta pills — a
     thin top divider plus a smaller, dimmer label keep it read as a
     secondary reference figure rather than a second, equally-weighted list. */
  .mat-total-row{margin-top:10px;padding-top:10px;border-top:1px dashed var(--border);}
  .mat-total-label{font-size:11px;color:var(--text-faint);font-weight:600;}
  .mat-total-list{margin-top:6px;opacity:.8;}
  .mat-total-list .mat-pill{padding:4px 8px 4px 4px;}
  .mat-total-list .mat-pill img{width:20px;height:20px;}

  .setpc-h{font-size:12.5px;font-weight:800;color:var(--accent-2);margin-bottom:4px;}
  .relic-piece{display:flex;gap:10px;padding:10px 0;border-bottom:1px solid var(--border);}
  .relic-piece:last-child{border-bottom:none;}

  .toast{
    position:fixed;left:50%;bottom:18px;transform:translateX(-50%);
    background:#241c04;color:var(--accent);border:1px solid var(--accent-dim);
    padding:9px 16px;border-radius:10px;font-size:13px;font-weight:600;z-index:100;
    box-shadow:0 8px 24px rgba(0,0,0,.4);
  }
  .hidden{display:none !important;}

  @media (min-width:520px){
    .grid{grid-template-columns:repeat(auto-fill,minmax(104px,1fr));}
  }

  /* ---------- desktop layout ----------
     Below this width the app stays the single mobile-style column, just
     centered — a fine, calm reading width. Above it, this goes considerably
     further than the previous pass (which only reworked the character
     detail page): a real screenshot-driven sweep of every view (grid, both
     detail layouts) turned up three concrete issues fixed here — (1) the
     category tab bar was the same tight mobile pill row just stretched
     across a much wider bar, leaving huge dead gaps between labels; (2) the
     grid kept its mobile card size, so a wide monitor just showed the same
     small cards with more empty margin, not a layout that used the space;
     (3) the sticky splash art had a real bug, not just a plainness issue —
     .splash-wrap is a grid item sharing its row with the (much taller) tab
     content next to it, and a grid item's default cross-axis stretch made
     the wrapper's own box grow to match that height while the <img> inside
     capped out at max-height, leaving its plain background color exposed
     as a dead gap below the art. align-self:start below is the fix — the
     wrapper now sizes to its own content, and *then* sticks correctly. */
  @media (min-width:880px){
    body{
      background:
        radial-gradient(1100px 760px at 18% -12%, #16204a 0%, transparent 60%),
        radial-gradient(760px 620px at 100% -6%, rgba(169,124,240,.14) 0%, transparent 55%),
        var(--bg);
    }
    #app{max-width:1280px;}
    .overlay-inner{max-width:1280px;}
    .grid{grid-template-columns:repeat(auto-fill,minmax(168px,1fr));gap:18px;}
    .card{padding:12px;border-radius:14px;}
    .card-name{font-size:13px;}

    /* The mobile bar is a tight pill strip built for ~5 short labels in a
       390px window; stretched across a desktop-width bar it just left huge
       gaps between labels with nothing to anchor them. An underlined row
       (the same language the character-detail subtabs already use one
       level down) reads as a real desktop nav instead of an enlarged mobile
       control, and stays left-aligned instead of spreading to fill the bar. */
    nav.tabs{
      background:none;border:none;border-radius:0;
      padding:0;margin-bottom:0;gap:28px;justify-content:flex-start;
    }
    .tab-indicator{display:none;}
    .tab{
      flex:0 0 auto;padding:0 0 14px;margin-bottom:-1px;font-size:14px;color:var(--text-dim);
      border-bottom:2px solid transparent;border-radius:0;
    }
    .tab.active{color:var(--text);border-bottom-color:var(--accent-purple);background:none;}
    .tab:not(.active):hover{color:var(--text);}

    /* Shared by every detail type (dhHeader() renders the same markup for
       characters/light cones/relics/items) so this only needs saying once. */
    #detailBody .dh-name{
      font-family:'Exo 2',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
      font-weight:600;font-size:26px;letter-spacing:.2px;margin:0 0 6px;
    }
    #detailBody .dh-thumb{width:72px;height:72px;}
    table.stat-table th,table.stat-table td{padding:9px 12px;}
    table.stat-table tr:nth-child(even){background:rgba(255,255,255,.025);}
    .mat-pill{padding:6px 12px 6px 6px;}
    .mat-pill img{width:30px;height:30px;}

    /* Light Cone/Relic/Planar/Material details have no splash art and so
       never enter the two-column grid below. A calm centered column (not
       the character page's two-column treatment: their icon is a small
       square, not a hero image, so mirroring that layout would leave an
       almost-empty left rail) but a size up from the plain mobile stack —
       bigger icon, name matched to the character page's, cards a touch
       roomier — so it doesn't read as a lesser, unfinished page next to it. */
    #detailBody:not(:has(.splash-wrap)){max-width:820px;margin:0 auto;padding:28px 26px 48px;}
    #detailBody:not(:has(.splash-wrap)) .dh{padding:0 0 22px;border-bottom:1px solid var(--border);margin-bottom:22px;align-items:center;}
    #detailBody:not(:has(.splash-wrap)) .dh-thumb{width:96px;height:96px;border-radius:18px;}
    #detailBody:not(:has(.splash-wrap)) .sec{margin:0 0 18px;padding:20px 24px;border-radius:16px;}
    #detailBody:not(:has(.splash-wrap)) .sec:last-child{margin-bottom:0;}

    /* The hero name below now carries the title, so on the character page
       specifically the hairline bar read as a second, competing header —
       dropped the duplicate label and the bar's own chrome so the back
       button just floats on the same cosmic backdrop as everything below. */
    .overlay-inner:has(.splash-wrap) .overlay-head{background:transparent;border-bottom:none;padding:18px 26px 0;}
    .overlay-inner:has(.splash-wrap) .oh-title{display:none;}

    /* ---- character detail: two-column hero layout ---- */
    #detailBody:has(.splash-wrap){
      display:grid;grid-template-columns:340px 1fr;gap:14px 32px;
      align-content:start;padding:14px 26px 48px;
    }
    #detailBody:has(.splash-wrap) > .dh{grid-column:1;grid-row:1;padding:2px 2px 0;border-bottom:none;align-items:flex-start;}
    #detailBody:has(.splash-wrap) > .splash-wrap{
      grid-column:1;grid-row:2;margin:0;position:sticky;top:20px;align-self:start;border-radius:20px;
      box-shadow:0 0 0 1px var(--ring,transparent), 0 30px 60px -22px var(--ring,transparent), 0 10px 30px rgba(0,0,0,.4);
    }
    #detailBody:has(.splash-wrap) > .splash-wrap img{max-height:480px;}
    #detailBody:has(.splash-wrap) > .subtabs{
      grid-column:2;grid-row:1;align-self:end;padding:8px 0 0;margin:0;gap:22px;
      border-bottom:1px solid var(--border);overflow-x:visible;
      -webkit-mask-image:none;mask-image:none;
    }
    #detailBody:has(.splash-wrap) > .subtabs .subtab{
      flex:0 0 auto;padding:0 0 12px;margin-bottom:-1px;border-radius:0;background:transparent;
      color:var(--text-dim);font-size:14px;border:none;border-bottom:2px solid transparent;
      transition:color .15s ease, border-color .15s ease;
    }
    #detailBody:has(.splash-wrap) > .subtabs .subtab:not(.active):hover{color:var(--text);}
    #detailBody:has(.splash-wrap) > .subtabs .subtab.active{
      background:transparent;color:var(--text);border-bottom-color:var(--accent-purple);
    }
    #detailBody:has(.splash-wrap) > div[id$="Mount"]{grid-column:2;grid-row:2;}

    /* See the block comment above — this is the alignment fix. */
    #detailBody:has(.splash-wrap) .sec{margin:0 0 18px;padding:20px 24px;border-radius:18px;}
    #detailBody:has(.splash-wrap) .sec:last-child{margin-bottom:0;}

    /* Profile reads as the page's opening line, not another data card —
       every other section here stays a functional card, so this is the
       one place that's allowed to look different. */
    #detailBody:has(.splash-wrap) .sec:has(> .lore){background:none;border:none;padding:2px 2px 6px;}
    #detailBody:has(.splash-wrap) .sec:has(> .lore) h3::before{display:none;}
    #detailBody:has(.splash-wrap) .sec:has(> .lore) .lore{font-size:15.5px;line-height:1.65;max-width:62ch;color:var(--text);}
  }

  @media (min-width:1400px){
    #app{max-width:1440px;}
    .overlay-inner{max-width:1440px;}
    .grid{grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:20px;}
  }

/* ---- usability additions (nothing here changes the default look) ---- */
:root{color-scheme:dark;}
html{scrollbar-gutter:stable;}
.card:focus-visible,.tab:focus-visible,.subtab:focus-visible,.fchip:focus-visible,.backBtn:focus-visible,.iconbtn:focus-visible,.lvl-btn:focus-visible,.copy-btn:focus-visible{
  outline:2px solid var(--accent-purple);outline-offset:2px;}
@media (pointer:coarse){
  /* bigger invisible hit areas for thumbs; the buttons look exactly the same */
  .tab,.subtab,.lvl-btn,.copy-btn,.fchip{position:relative;}
  .tab::after,.subtab::after{content:'';position:absolute;left:0;right:0;top:-8px;bottom:-8px;}
  .lvl-btn::after,.copy-btn::after,.fchip::after{content:'';position:absolute;inset:-7px;}
  .searchRow input{font-size:16px;} /* stops iOS zooming in on focus, so pinch-zoom can stay enabled */
}
@media (max-width:879px){
  /* the tab row follows you down a long character page instead of scrolling away */
  #detailBody > .subtabs{position:sticky;top:-12px;z-index:6;background:var(--bg);}
}
@media (max-height:500px) and (orientation:landscape){
  header.topbar{position:static;} /* a sticky header would eat ~40% of a sideways phone screen */
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms !important;transition-duration:.01ms !important;scroll-behavior:auto !important;}
  .spinner,.iconbtn.spinning{animation-duration:.9s !important;animation-iteration-count:infinite !important;}
}

/* ---- bonus traces: one total per stat, each opens to show the traces behind it ---- */
.bstat{background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius-sm);margin-bottom:8px;overflow:hidden;}
.bstat summary{display:flex;align-items:center;gap:10px;padding:12px 14px;cursor:pointer;list-style:none;-webkit-tap-highlight-color:transparent;}
.bstat summary::-webkit-details-marker{display:none;}
.bstat-name{flex:1;min-width:0;font-weight:600;font-size:14px;}
.bstat-n{font-size:11.5px;color:var(--text-faint);}
.bstat-total{font-weight:700;color:var(--accent);font-size:14.5px;min-width:56px;text-align:right;}
.bstat-chev{color:var(--text-faint);font-size:18px;line-height:1;transition:transform .2s;}
.bstat[open] .bstat-chev{transform:rotate(90deg);}
.bstat-list{border-top:1px solid var(--border);padding:4px 14px 8px;}
.bstat-part{display:flex;justify-content:space-between;gap:10px;padding:8px 0;font-size:13px;border-bottom:1px dashed var(--border);}
.bstat-part:last-child{border-bottom:none;}
.bstat-pname small{display:block;color:var(--text-faint);font-size:11.5px;margin-top:1px;}
.bstat-pval{font-weight:600;white-space:nowrap;}
@media (hover:hover) and (pointer:fine){ .bstat summary:hover{background:var(--surface-hover);} }
