
  :root{
    --bg-base:#12141a;
    --bg-surface:#1a1d24;
    --bg-surface-2:#20242c;
    --bg-surface-3:#262b35;
    --border:#2b3038;
    --border-soft:#22262e;
    --text-primary:#e9ebef;
    --text-secondary:#959daa;
    --text-tertiary:#5e6675;
    --accent:#4f8cff;
    --accent-soft:rgba(79,140,255,.14);
    --accent-soft-2:rgba(79,140,255,.28);
    --success:#34d399;
    --success-soft:rgba(52,211,153,.14);
    --warning:#f0a050;
    --warning-soft:rgba(240,160,80,.14);
    --danger:#f0566a;
    --danger-soft:rgba(240,86,106,.14);
    --radius:10px;
    --radius-sm:6px;
    --radius-lg:14px;
    --ps-triangle:rgb(107,228,223);
    --ps-circle:rgb(247,95,102);
    --ps-cross:rgb(118,126,225);
    --bind-yellow:#f0c419;
    --topbar-bg:rgba(18,20,26,.9);
    --tree-complete-fill:#1a3328;
    --tree-hover-fill:#332b10;   /* prereq-trace highlight (dark amber on dark) */
    /* Admin "has written content" marker — violet, distinct from accent blue,
       hover yellow, and completed green. */
    --content:#a78bfa;
    --content-soft:rgba(167,139,250,.15);
    --tree-content-fill:#241d3a;
    /* mechanic-goal route (fuchsia — the last unclaimed hue; states on the
       route differ by weight, not extra colours) */
    --goal:#e879f9;
    --goal-soft:rgba(232,121,249,.16);
    --on-accent:#0c1320;
    --sidebar-w:212px;
    --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Consolas, monospace;
  }

  /* Light theme: same variable names, re-valued. Because every surface,
     border and text color in the app reads from these variables, flipping
     this class on <body id="artifacts-component-root-html"> restyles the whole UI. Backgrounds ascend from a
     soft grey base through near-whites; text darkens; borders darken enough
     to read on light; the accent deepens slightly for contrast and its
     soft tints get a touch more opacity since they now sit on light. */
  body.light-mode{
    --bg-base:#f4f6fa;
    --bg-surface:#ffffff;
    --bg-surface-2:#f0f2f7;
    --bg-surface-3:#e7eaf1;
    --border:#d5dae3;
    --border-soft:#e4e8ef;
    --text-primary:#1a1d24;
    --text-secondary:#5a6472;
    --text-tertiary:#8a94a4;
    --accent:#2f6bdd;
    --accent-soft:rgba(47,107,221,.10);
    --accent-soft-2:rgba(47,107,221,.24);
    --success:#12a86e;
    --success-soft:rgba(18,168,110,.12);
    --warning:#c9791a;
    --warning-soft:rgba(201,121,26,.14);
    --danger:#d63a4d;
    --danger-soft:rgba(214,58,77,.12);
    --ps-triangle:rgb(20,150,145);
    --ps-circle:rgb(210,60,68);
    --ps-cross:rgb(78,88,190);
    --bind-yellow:#b8930a;
    --topbar-bg:rgba(255,255,255,.85);
    --tree-complete-fill:#d6f0e2;
    --tree-hover-fill:#faecc0;    /* pale gold — matches the yellow stroke on light */
    --content:#7c5ce0;
    --content-soft:rgba(124,92,224,.12);
    --tree-content-fill:#ece7fb;
    --goal:#a21caf;
    --goal-soft:rgba(162,28,175,.11);
    --on-accent:#ffffff;
    color-scheme:light;
  }
  /* Overall completion bar: a brighter, poppier green in light mode. The
     tokenised --success stays muted (it drives toggles, checks and completed
     states); only the headline progress bar gets the vivid green. */
  body.light-mode .completion-bar-fill{ background:rgb(51,255,51); }
  /* Settings on/off toggles: a much lighter grey track in light mode. The knob
     goes white (with a soft shadow) so it still reads against the lighter track,
     since the base knob grey would otherwise blend in. */
  body.light-mode .bool-toggle .toggle-track{ background:#d3d8e0; }
  body.light-mode .bool-toggle .toggle-knob{ background:#ffffff; box-shadow:0 1px 2px rgba(20,30,50,.18); }
  /* Completion tickboxes: a brighter green in light mode — sits between the
     muted tokenised --success (#12a86e) and the vivid progress bar (51,255,51).
     Scoped to the checkbox so toggles/completed states keep the muted token. */
  body.light-mode .item-check.checked{ border-color:#22d34f; color:#22d34f; background:rgba(34,211,79,.16); }
  body.light-mode .rank-name{ color:var(--rank-name-light); text-shadow:0 0 18px rgba(var(--rank-main),0.25); }
  body.light-mode .rank-eyebrow{ color:var(--rank-eyebrow-light); }
  body.light-mode .rank-hero::before{
    background:linear-gradient(180deg,
      rgba(var(--rank-light),0.30) 0%,
      rgba(var(--rank-light),0.20) 30%,
      rgba(var(--rank-light),0.11) 62%,
      rgba(var(--rank-light),0.04) 86%,
      var(--bg-base) 100%);
  }


  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;}
  body{
    background:var(--bg-base);
    color:var(--text-primary);
    font-family:var(--font-body);
    font-size:14.5px;
    line-height:1.55;
    -webkit-font-smoothing:antialiased;
  }
  *:focus-visible{outline:2px solid var(--accent); outline-offset:2px; border-radius:4px;}
  @media (prefers-reduced-motion: reduce){
    *{transition:none !important; animation:none !important;}
  }
  button{font-family:inherit; color:inherit;}
  a{color:inherit; text-decoration:none;}
  ::selection{background:var(--accent-soft-2);}

  /* ---------- shell ---------- */
  .app-shell{display:flex; min-height:100vh;}

  .sidebar{
    /* The rank hero's gradient band is an absolutely positioned ::before that
       deliberately extends left to meet the sidebar's edge. It computes that
       reach from 50vw — which includes the scrollbar — so on a narrow window
       it overshoots and paints across the sidebar. Both are positioned
       elements and the hero comes later in the DOM, so it won a paint order
       it should never have been in. Lifting the sidebar settles it for good,
       whatever the arithmetic does. */
    z-index:2;
    width:var(--sidebar-w);
    flex-shrink:0;
    background:var(--bg-surface);
    border-right:1px solid var(--border-soft);
    display:flex;
    flex-direction:column;
    padding:calc(20px + env(safe-area-inset-top, 0px)) 12px 20px 12px;
    position:sticky;
    top:0;
    height:100vh;   /* fallback for browsers without dvh */
    /* dvh tracks Safari's chrome as it shows/hides on scroll, so the sidebar is
       always exactly the *visible* height — the Axial logo never ends up under
       the tab bar and the account button never falls off the bottom. */
    height:100dvh;
  }
  .brand{
    display:flex; justify-content:center; align-items:center;
    padding:16px 10px 32px 10px;
    /* Anchor for the "By Viizd" credit, which is absolutely positioned into
       the existing 32px of bottom padding. Keeping it out of the flex flow is
       deliberate: the gap between the logo and the nav list must not change
       whether the credit is showing or not. */
    position:relative;
  }
  .brand-text{
    display:flex; align-items:center;
    color:var(--text-primary);
    cursor:pointer;
  }
  .brand-credit{
    position:absolute; left:0; right:0; bottom:14px;
    text-align:center; pointer-events:none;
    font-family:var(--font-mono); font-size:9.5px; font-weight:700;
    letter-spacing:.20em; text-transform:uppercase;
    color:rgba(255,255,255,0.42);
  }
  /* The entrance animation is opt-in via a second class, applied only on the
     render that actually reveals the credit. The sidebar is rebuilt on every
     click and every tab change, so animating .brand-credit itself would replay
     the fade-in each time — the credit would flicker whenever you navigated. */
  .brand-credit-reveal{
    animation:brandCreditIn .5s cubic-bezier(.2,.8,.25,1) both;
  }
  body.light-mode .brand-credit{ color:rgba(30,35,45,0.45); }
  @keyframes brandCreditIn{
    from{opacity:0; transform:translateY(-4px);}
    to{opacity:1; transform:none;}
  }
  @media (prefers-reduced-motion: reduce){
    .brand-credit-reveal{ animation:none; }
  }

  /* ---------- wordmark logo ----------
     Layered SVG letterforms (custom geometric paths) driving a three-stage
     hover: (1) the blue mark fades out while (2) a gold line draws the
     letter outlines (JS progress tween, so leaving mid-draw retraces
     backwards from wherever it got to), then (3) on completion the letters
     flood gold with a glow that settles. Leaving while gold simply
     cross-fades back to blue. Rainbow easter egg swaps in its own layer. */
  .brand-word{display:inline-block; line-height:1;}
  /* Type-set wordmark. Uses the same blue->gold gradient the drawn glyphs had,
     clipped to the text so the brand colour survives the change. */
  
  .bw-svg{height:34px; width:auto; display:block; overflow:visible;}
  .bw-blue{opacity:1; transition:opacity .22s ease; filter:drop-shadow(0 0 7px rgba(90,150,240,0.35));}
  .bw-blue path, .bw-blue text{fill:url(#bwBlueGrad);}
  .brand-word.bw-active .bw-blue{opacity:0;}
  .bw-outline{opacity:1; transition:opacity .28s ease;}
  .bw-outline path, .bw-outline text{fill:none; stroke:rgb(255,196,0); stroke-width:5; stroke-linejoin:round; stroke-linecap:round;}
  .brand-word.bw-filled .bw-outline{opacity:0; transition-delay:.12s;}
  .bw-gold{opacity:0; transition:opacity .2s ease;}
  .bw-gold path, .bw-gold text{fill:url(#bwGoldGrad);}
  .brand-word.bw-filled .bw-gold{opacity:1; animation:bwGlowSettle 1.15s ease-out both;}
  @keyframes bwGlowSettle{
    0%{filter:drop-shadow(0 0 0 rgba(255,196,0,0));}
    28%{filter:drop-shadow(0 0 15px rgba(255,196,0,0.85));}
    100%{filter:drop-shadow(0 0 4px rgba(255,196,0,0.3));}
  }
  /* Gradient stops via CSS so light mode can deepen the palette */
  .bw-b1{stop-color:#79aeff;} .bw-b2{stop-color:#4f8cff;} .bw-b3{stop-color:#3c78e0;}
  .bw-g1{stop-color:#ffd34d;} .bw-g2{stop-color:#ffc400;}
  body.light-mode .bw-b1{stop-color:#4d86e8;} body.light-mode .bw-b2{stop-color:#2f6bdd;} body.light-mode .bw-b3{stop-color:#2a63cf;}
  body.light-mode .bw-g1{stop-color:#eab000;} body.light-mode .bw-g2{stop-color:#e0a800;}
  body.light-mode .bw-blue{filter:drop-shadow(0 0 6px rgba(47,107,221,0.25));}
  @media (prefers-reduced-motion: reduce){
    .bw-blue, .bw-outline, .bw-gold{ transition:none; }
    .brand-word.bw-filled .bw-gold{ animation:none; filter:drop-shadow(0 0 4px rgba(255,196,0,0.3)); }
  }
  .brand-text svg{height:40px; width:auto; display:block; overflow:visible;}

  /* flex:1 fills the gap between brand and footer; min-height:0 + overflow lets
     the links scroll on their own when the viewport is too short for all of
     them (e.g. iPad landscape with tabs showing), keeping brand and the account
     button pinned instead of pushing the footer off-screen. */
  .nav-list{display:flex; flex-direction:column; gap:2px; flex:1; min-height:0; overflow-y:auto;}
  .nav-item{
    display:flex; align-items:center; gap:10px;
    padding:9px 10px;
    border-radius:var(--radius-sm);
    color:var(--text-secondary);
    font-size:13.5px; font-weight:500;
    cursor:pointer; border:none; background:none; width:100%; text-align:left;
    position:relative; overflow:hidden;
  }
  .nav-item svg{width:17px;height:17px; flex-shrink:0; opacity:.85;}
  .nav-item:hover{background:var(--bg-surface-2); color:var(--text-primary);}
  .nav-item.active{background:var(--accent-soft); color:var(--accent);}
  .nav-item.active svg{opacity:1;}
  /* Intro-pending badge: a short gradient wash hugging the right edge of the
     tab (in the Axial logo hover yellow) that fades inward, plus a solid dot
     pinned to the right. The gradient sits behind the label via a pseudo. */
  .nav-item.has-intro::after{
    content:""; position:absolute; top:0; right:0; bottom:0; width:70px;
    background:linear-gradient(to left, rgba(255,196,0,0.28), rgba(255,196,0,0));
    pointer-events:none;
  }
  .nav-item .nav-intro-badge{
    position:absolute; right:10px; top:50%; transform:translateY(-50%);
    width:8px; height:8px; border-radius:50%;
    background:rgb(255,196,0);
    box-shadow:0 0 6px rgba(255,196,0,0.8);
    z-index:1; animation:navIntroPulse 2s ease-in-out infinite;
  }
  @keyframes navIntroPulse{
    0%,100%{ opacity:1; }
    50%{ opacity:0.45; }
  }

  .sidebar-footer{padding-top:10px; margin-top:8px;}
  /* Divider now sits between the socials row and the account widget (was above
     the whole footer). Placed within the original 10px gap — 5px padding under
     the icons for the line, 5px margin below it — so the total icon-to-account
     distance stays 10px, unchanged from before. */
  .socials-row{ display:flex; gap:6px; justify-content:center; padding-bottom:5px; margin-bottom:5px; border-bottom:1px solid var(--border-soft); }
  .social-btn{
    display:inline-flex; align-items:center; justify-content:center;
    width:30px; height:30px; border-radius:8px; cursor:pointer;
    background:none; border:1px solid transparent;
    color:var(--text-tertiary,rgba(255,255,255,0.42));
    transition:color .14s, background .14s, border-color .14s;
  }
  .social-btn svg{ width:17px; height:17px; }
  .social-btn:hover{
    color:var(--text-primary);
    background:var(--surface-2,rgba(255,255,255,0.05));
    border-color:var(--border,rgba(255,255,255,0.09));
  }
  .social-edit:hover{ color:var(--accent,#ffc400); }
  .credits-intro{ font-size:14px; line-height:1.6; color:var(--text-secondary); margin:0 0 14px; }
  .modal-title svg{ width:18px; height:18px; flex-shrink:0; }
  .modal-title{ display:inline-flex; align-items:center; gap:8px; }
  .credits-title-heart{ color:#ff5b6e; display:inline-flex; }
  .credits-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:11px; }
  .credits-item{ display:flex; flex-direction:column; gap:1px; }
  .credits-name{ font-size:14px; font-weight:700; color:var(--text-primary); }
  .credits-note{ font-size:12.5px; color:var(--text-tertiary,rgba(255,255,255,0.5)); }
  .thanks-edit-row{ display:flex; gap:6px; margin-bottom:7px; align-items:center; }
  .thanks-edit-row .wizard-input:first-child{ max-width:150px; }
  .account-btn{
    display:flex; align-items:center; gap:10px;
    padding:8px; border-radius:var(--radius-sm);
    background:none; border:none; cursor:pointer; width:100%; text-align:left;
    color:var(--text-secondary);
  }
  .account-btn:hover{background:var(--bg-surface-2);}
  .account-avatar{
    width:34px; height:34px; border-radius:50%; flex-shrink:0; overflow:hidden;
    display:flex; align-items:center; justify-content:center;
  }
  .account-avatar svg{width:100%; height:100%; display:block;}
  .account-btn-text{display:flex; flex-direction:column; min-width:0; line-height:1.25;}
  .account-btn-name{font-size:13px; font-weight:700; color:var(--text-primary); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}
  .account-btn-sub{font-size:11px; color:var(--text-tertiary);}
  .admin-toggle{
    display:flex; align-items:center; gap:9px;
    padding:8px 10px; border-radius:var(--radius-sm);
    color:var(--text-tertiary); font-size:12px; font-weight:500;
    background:none; border:none; cursor:pointer; width:100%; text-align:left;
    opacity:.65;
  }
  .admin-toggle:hover{opacity:1; background:var(--bg-surface-2); color:var(--text-secondary);}
  .admin-toggle svg{width:14px;height:14px;}
  .brand{cursor:pointer;}

  /* ---- account modal ---- */
  /* Profile block leads the account menu — centred column so the mark and name
     carry the header rather than sitting as a small row above a list. */
  .acct-profile{display:flex; flex-direction:column; align-items:center; text-align:center; gap:0; margin:4px 0 24px;}
  .acct-profile-avatar{width:88px; height:88px; border-radius:50%; overflow:hidden; flex-shrink:0; margin-bottom:12px;}
  .acct-profile-avatar svg{width:100%; height:100%; display:block;}
  .acct-profile-name{font-size:22px; font-weight:800; letter-spacing:-.01em;}
  .acct-profile-sub{font-size:12.5px; color:var(--text-tertiary); margin-top:3px;}
  .acct-setting-row{
    display:flex; align-items:center; justify-content:space-between; gap:14px;
    padding:13px 2px; border-top:1px solid var(--border-soft);
  }
  .acct-setting-row:last-child{border-bottom:1px solid var(--border-soft);}
  /* patch notes */
  .pn-add-btn{ margin-bottom:14px; }
  .pn-editor{ margin-bottom:16px; }
  .pn-entry{ padding:14px 0; border-bottom:1px solid var(--border-soft); }
  .pn-entry:last-child{ border-bottom:none; }
  .pn-entry-head{ display:flex; align-items:center; gap:10px; }
  .pn-version{ font-size:15px; font-weight:800; color:var(--text-primary); }
  .pn-date{ font-size:12px; color:var(--text-tertiary,rgba(255,255,255,0.45)); font-family:var(--font-mono); }
  .pn-del{ margin-left:auto; }
  .pn-changes{ margin:8px 0 0; padding-left:20px; display:flex; flex-direction:column; gap:5px; }
  .pn-changes li{ font-size:13.5px; line-height:1.5; color:var(--text-secondary); }
  .acct-setting-label{font-size:13.5px; font-weight:600; color:var(--text-primary);}
  .acct-setting-desc{font-size:11.5px; color:var(--text-tertiary); margin-top:2px;}
  .acct-row-btn{
    background:var(--bg-surface-2); border:1px solid var(--border); color:var(--text-secondary);
    border-radius:var(--radius-sm); padding:7px 12px; cursor:pointer; font-size:12.5px; font-weight:600;
    display:flex; align-items:center; gap:7px; white-space:nowrap;
  }
  .acct-row-btn:hover{color:var(--text-primary); border-color:var(--accent-soft-2);}
  .acct-row-btn svg{width:13px; height:13px;}
  .acct-data-btns{display:flex; gap:8px; flex-shrink:0;}
  .acct-reset-intros + .acct-reset-intros{margin-top:8px;}
  .acct-reset-intros{
    width:100%; margin-top:18px; padding:10px; border-radius:var(--radius-sm);
    background:var(--bg-surface-2); border:1px solid var(--border); color:var(--text-secondary);
    cursor:pointer; font-size:13px; font-weight:600;
  }
  .acct-reset-intros:hover{color:var(--text-primary); border-color:var(--accent-soft-2);}
  .acct-signout{
    width:100%; margin-top:10px; padding:10px; border-radius:var(--radius-sm);
    background:none; border:1px solid var(--border); color:var(--danger, #e5484d);
    cursor:pointer; font-size:13px; font-weight:600;
  }
  .acct-signout:hover{background:rgba(229,72,77,0.08); border-color:rgba(229,72,77,0.5);}

  /* Support Axial row + page */
  /* Navigable rows: the whole row is the target, with a chevron as the only
     affordance. Padding is pulled out to the panel edge so the hover tint
     reads as the row lighting up rather than a floating rectangle. */
  .acct-nav-row{
    cursor:pointer; margin:0 -10px; padding-left:10px; padding-right:10px;
    border-radius:9px; transition:background .14s ease;
  }
  .acct-nav-row:hover{background:var(--bg-surface-2);}
  .acct-nav-row:focus-visible{outline:2px solid var(--accent); outline-offset:-2px;}
  .acct-nav-chevron{
    display:inline-flex; flex-shrink:0; color:var(--text-tertiary);
    transition:color .14s ease, transform .14s ease;
  }
  .acct-nav-chevron svg{width:17px; height:17px;}
  .acct-nav-row:hover .acct-nav-chevron{color:var(--accent); transform:translateX(2px);}
  .acct-support-row{cursor:pointer; transition:border-color .14s ease;}
  .acct-support-row .acct-setting-label{display:inline-flex; align-items:center; gap:7px;}
  .acct-support-row .acct-setting-label svg{width:14px; height:14px; color:#e0596a;}
  .modal-back{
    display:inline-flex; align-items:center; justify-content:center;
    width:30px; height:30px; padding:0; margin-right:2px;
    background:none; border:none; border-radius:8px; cursor:pointer; color:var(--text-secondary);
  }
  .modal-back svg{width:18px; height:18px;}
  .modal-back:hover{background:var(--bg-surface-3); color:var(--text-primary);}

  .support-hero-text{
    display:flex; gap:10px; align-items:flex-start;
    margin:0 0 20px; font-size:13.5px; line-height:1.6; color:var(--text-secondary);
  }
  .support-hero-heart{flex-shrink:0; color:#e0596a; display:inline-flex; margin-top:1px;}
  .support-hero-heart svg{width:16px; height:16px;}


  .support-chips{display:grid; grid-template-columns:repeat(5,1fr); gap:8px; margin-bottom:22px;}
  .support-chip{
    display:flex; align-items:center; justify-content:center;
    height:46px; padding:0 4px; border-radius:10px; cursor:pointer;
    background:var(--bg-surface); border:1.5px solid var(--border-soft);
    color:var(--text-primary); font-size:15px; font-weight:700;
    transition:border-color .14s ease, background .14s ease;
  }
  .support-chip:hover{border-color:var(--accent-soft-2);}
  .support-chip.selected{border-color:var(--accent); background:var(--accent-soft); color:var(--accent);}
  .support-chip-custom{font-size:13px; font-weight:600; color:var(--text-secondary); gap:1px;}
  .support-chip-custom.selected{color:var(--accent);}
  .support-custom-symbol{font-size:14px; font-weight:600; opacity:0.8;}
  .support-custom-input{
    width:100%; min-width:0; border:none; background:none; outline:none;
    color:inherit; font-size:15px; font-weight:700; text-align:center; padding:0;
  }

  .support-note{
    padding:11px 13px; margin-bottom:16px; border-radius:10px;
    background:var(--accent-soft); border:1px solid var(--accent-soft-2);
    font-size:12.5px; line-height:1.5; color:var(--text-secondary);
  }
  .support-cta{width:100%; justify-content:center; padding:13px; font-size:14.5px;}
  .support-fineprint{margin-top:11px; text-align:center; font-size:11.5px; color:var(--text-tertiary); line-height:1.5;}

  /* First-visit prompt */
  .firstvisit-emoji{line-height:0; margin-bottom:14px;}
  .firstvisit-emoji img{width:64px; height:64px; object-fit:contain; display:inline-block; filter:drop-shadow(0 4px 12px rgba(38,120,240,0.35));}
  .firstvisit-title{font-size:20px; font-weight:800; margin-bottom:8px;}
  .firstvisit-text{font-size:13.5px; color:var(--text-secondary); line-height:1.55; margin:0 auto 22px; max-width:320px;}
  .firstvisit-btns{display:flex; gap:10px;}
  .firstvisit-btn{flex:1; justify-content:center; padding:11px;}

  /* Intro popup */
  .intro-media{
    width:100%; border-radius:var(--radius-sm); margin-bottom:16px; overflow:hidden;
  }
  .intro-media-empty{
    display:flex; align-items:center; justify-content:center;
    aspect-ratio:16/7; background:var(--bg-surface-2); border:1px dashed var(--border);
    color:var(--text-tertiary);
  }
  .intro-media-empty svg{width:34px; height:34px; opacity:.5;}
  .intro-body{font-size:13.5px; color:var(--text-secondary); line-height:1.6; margin:0;}
  .intro-ok-btn{min-width:120px; justify-content:center;}
  .acct-back-link{
    display:inline-flex; align-items:center; gap:6px; cursor:pointer;
    color:var(--text-tertiary); font-size:12.5px; font-weight:600; margin-bottom:4px; background:none; border:none; padding:0;
  }
  .acct-back-link:hover{color:var(--text-secondary);}
  .acct-back-link svg{width:13px; height:13px;}

  .main{flex:1; display:flex; flex-direction:column; min-width:0;}

  .topbar{
    display:flex; align-items:center; gap:16px;
    padding:calc(14px + env(safe-area-inset-top, 0px)) 28px 14px 28px;
    border-bottom:1px solid var(--border-soft);
    position:sticky; top:0; z-index:20;
    background:var(--topbar-bg);
    backdrop-filter:blur(6px);
  }
  .crumb{font-size:12px; color:var(--text-tertiary); font-weight:600; letter-spacing:.04em; text-transform:uppercase; white-space:nowrap;}

  /* Topbar account button — the sidebar owns the account button on desktop,
     so this compact avatar only appears on mobile where the sidebar is gone. */
  .topbar-account{display:none; flex-shrink:0; padding:0; background:none; border:none; cursor:pointer;}
  .topbar-account-avatar{
    width:32px; height:32px; border-radius:50%; overflow:hidden; display:block;
    box-shadow:0 0 0 1px var(--border);
  }
  .topbar-account-avatar svg{width:100%; height:100%; display:block;}
  .topbar-account:hover .topbar-account-avatar{box-shadow:0 0 0 2px var(--accent-soft-2);}

  .search-wrap{position:relative; flex:1; max-width:420px; margin-left:auto;}
  .search-input{
    width:100%; background:var(--bg-surface-2); border:1px solid var(--border);
    border-radius:var(--radius-sm); color:var(--text-primary);
    padding:8px 12px 8px 34px; font-size:13.5px;
  }
  .search-input::placeholder{color:var(--text-tertiary);}
  .search-input:focus{border-color:var(--accent-soft-2);}
  .search-icon{position:absolute; left:11px; top:50%; transform:translateY(-50%); width:15px;height:15px; color:var(--text-tertiary); pointer-events:none;}
  .search-dropdown{
    position:absolute; top:calc(100% + 6px); left:0; right:0;
    background:var(--bg-surface-2); border:1px solid var(--border);
    border-radius:var(--radius); box-shadow:0 12px 28px rgba(0,0,0,.45);
    max-height:360px; overflow-y:auto; z-index:30;
  }
  .search-group-label{padding:9px 14px 4px; font-size:10.5px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--text-tertiary);}
  .search-result{
    display:flex; align-items:center; justify-content:space-between; gap:10px;
    padding:9px 14px; cursor:pointer; font-size:13.5px;
  }
  .search-result:hover{background:var(--bg-surface-3);}
  .search-empty{padding:18px 14px; font-size:13px; color:var(--text-tertiary);}
  .search-footer{padding:8px 14px; font-size:12px; color:var(--accent); border-top:1px solid var(--border-soft); cursor:pointer; display:flex; align-items:center; gap:5px;}
  .search-footer svg{width:12px; height:12px; flex-shrink:0;}
  .search-footer:hover{background:var(--bg-surface-3);}

  .content{padding:32px; max-width:1080px; margin:0 auto; width:100%;}

  /* ---------- entrance animations ----------
     On genuine navigation the content area fades and rises into place; its
     items can cascade in via JS-assigned delays. Same-view re-renders (hover,
     toggles) skip this entirely, and reduced-motion disables it. */
  @keyframes viewEnter{ from{opacity:0; transform:translateY(8px);} to{opacity:1; transform:translateY(0);} }
  @keyframes itemEnter{ from{opacity:0; transform:translateY(10px);} to{opacity:1; transform:translateY(0);} }
  .content-enter{ animation:viewEnter .26s cubic-bezier(.33,0,.2,1) both; }
  /* When items are individually staggered, the wrapper itself shouldn't also
     slide — it just fades so the children carry the movement. */
  .content-enter:has(.stagger-item){ animation:viewEnter .2s ease both; }
  .stagger-item{ animation:itemEnter .32s cubic-bezier(.33,0,.2,1) both; }
  /* List/tile view toggle: the wrapper deliberately carries NO animation of its
     own, so the page title and intro stay put — only the rows (.stagger-item,
     added by staggerContentEntrance) replay. It's purely a hook for that pass;
     don't give it a viewEnter animation or the header will re-animate too. */
  .content-stagger-only{ animation:none; }
  @media (prefers-reduced-motion: reduce){
    .content-enter, .content-enter:has(.stagger-item), .stagger-item{ animation:none; }
    .tree-node-card, .tree-node-card:hover,
    .tree-node-rect, .tree-node-tier, .tree-node-label,
    .tree-node-card:hover .tree-node-rect,
    .tree-node-card:hover .tree-node-tier,
    .tree-node-card:hover .tree-node-label{ transition:none; transform:none; }
  }

  /* ---------- bottom nav (mobile) ---------- */
  .bottom-nav{display:none;}

  /* ---------- generic bits ---------- */
  .page-title{font-size:22px; font-weight:700; margin:0 0 4px;}
  .page-sub{color:var(--text-secondary); font-size:13.5px; margin:0 0 26px;}
  /* Editable page description (admin). The pencil sits inline at the end of
     the text rather than in a fixed corner, so it never collides with the
     header controls on narrow screens. */
  .page-sub-edit-btn{width:22px; height:22px; margin-left:8px; vertical-align:middle; display:inline-flex;}
  .page-sub-edit-btn svg{width:11px; height:11px;}
  .page-sub-editor{margin:0 0 26px; max-width:680px;}
  .page-sub-input{font-size:13.5px; min-height:64px;}
  .page-sub-actions{display:flex; gap:8px; margin-top:8px; flex-wrap:wrap;}
  .btn-sm{padding:6px 11px; font-size:12.5px;}
  .section-label{font-size:11px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; color:var(--text-tertiary); margin:0 0 12px;}
  .section-label.subheading-lg{font-size:14.5px; letter-spacing:.03em; color:var(--text-secondary);}

  .card{
    background:var(--bg-surface); border:1px solid var(--border-soft);
    border-radius:var(--radius-lg); padding:20px;
  }
  .clickable{cursor:pointer;}
  .clickable:hover{border-color:var(--border);}

  .chip{
    display:inline-flex; align-items:center; gap:5px;
    padding:4px 10px; border-radius:999px;
    background:var(--bg-surface-3); border:1px solid var(--border);
    font-size:12px; font-weight:500; color:var(--text-secondary);
    cursor:pointer; white-space:nowrap;
  }
  .chip:hover{border-color:var(--accent-soft-2); color:var(--text-primary);}
  .chip.complete{color:var(--success);}
  .chip-dot{width:6px;height:6px;border-radius:50%;background:var(--text-tertiary); flex-shrink:0;}
  .chip.complete .chip-dot{background:var(--success);}

  .tier-badge{
    font-family:var(--font-mono); font-size:10.5px; font-weight:700;
    letter-spacing:.06em; text-transform:uppercase;
    padding:3px 8px; border-radius:4px;
    background:var(--bg-surface-3); color:var(--text-secondary);
    border:1px solid var(--border);
    display:inline-flex; align-items:center; gap:5px;
  }

  /* boost meter — signature progress element: discrete segments, no gradients */
  .boost-meter{display:flex; gap:3px; align-items:center;}
  .boost-meter .seg{height:9px; flex:1; background:var(--bg-surface-3); border-radius:2px;}
  .boost-meter .seg.filled{background:var(--accent);}
  .boost-meter.green .seg.filled{background:var(--success);}
  .boost-meter-row{display:flex; align-items:center; gap:12px;}
  .boost-meter-row .boost-meter{flex:1;}
  .boost-pct{font-family:var(--font-mono); font-weight:700; font-size:15px; min-width:42px; text-align:right;}


  /* ---------- dashboard rank hero ---------- */
  .rank-hero{
    position:relative;
    /* Margin pulls the hero out to the .content container's own padding so
       it sits flush against the bottom of the topbar, with no gap. The
       gradient itself extends further (full .main width) via the ::before
       pseudo-element below. */
    margin:-32px -32px 24px;
    padding:36px 32px 28px;
    text-align:center;
  }
  .rank-hero::before{
    /* Gradient is its own element rather than a background on .rank-hero so
       it can be sized to land *exactly* at the bottom of the rank icon area
       — the rank name and preview text sit on a gentle fade of red below.
       The horizontal extension uses negative left/right offsets so the band
       reaches from .main's left edge (just past the sidebar) all the way
       to the viewport's right edge regardless of how wide the page is,
       even when .content is capped at its max-width on very wide screens. */
    content:""; position:absolute; top:0;
    left:calc(var(--sidebar-w)/2 - 50vw + 50%);
    right:calc(var(--sidebar-w)/2 - 50vw + 50%);
    height:345px;
    /* Driven by --rank-deep so the band re-themes with the selected rank. */
    background:linear-gradient(180deg,
      rgba(var(--rank-deep),0.60) 0%,
      rgba(var(--rank-deep),0.45) 28%,
      rgba(var(--rank-deep),0.24) 62%,
      rgba(var(--rank-deep),0.07) 86%,
      var(--bg-base) 100%);
    pointer-events:none;
  }
  .rank-hero::after{
    /* Faint radial spotlight behind the icon; subtle enough not to fight
       the icon's own glow. max-width:100vw stops it from ever extending
       past the viewport edge on narrow screens (it was 520px wide which
       would otherwise overflow on phones around 480px wide). */
    content:""; position:absolute; left:50%; top:6%;
    width:520px; max-width:100vw; height:300px; transform:translateX(-50%);
    background:radial-gradient(ellipse at center, rgba(var(--rank-main),0.12) 0%, rgba(var(--rank-main),0) 65%);
    pointer-events:none;
  }
  .rank-hero-inner{position:relative; display:flex; flex-direction:column; align-items:center; gap:10px;}
  .rank-eyebrow{
    font-family:var(--font-mono); font-size:10.5px; font-weight:700;
    letter-spacing:.18em; text-transform:uppercase;
    color:rgba(255,255,255,0.62);
  }
  .rank-eyebrow .rank-eyebrow-dot{
    display:inline-block; width:6px; height:6px; border-radius:50%;
    background:rgb(var(--rank-main)); margin-right:7px; vertical-align:1px;
    box-shadow:0 0 8px rgba(var(--rank-main),0.7);
  }
  .rank-img-wrap{
    width:176px; height:176px; display:flex; align-items:flex-end; justify-content:center;
    /* Perspective is what turns the rotateX/rotateY on the inner wrapper into
       an actual sense of the badge turning in space rather than a flat skew.
       Lower values = more dramatic foreshortening; 700px reads as "held in
       your hand" without the edges warping into caricature. */
    perspective:700px;
    position:relative;
  }
  .rank-img-wrap img{width:100%; height:100%; object-fit:contain; display:block;}
  .rank-img-3d{
    position:relative; width:100%; height:100%;
    transform-style:preserve-3d;
    /* Slow, eased settle when the cursor leaves. While the cursor is *inside*
       the badge we swap to the near-instant transition below, so the rotation
       tracks the pointer instead of lagging behind it. */
    transition:transform 380ms cubic-bezier(.2,.8,.25,1);
    will-change:transform;
  }
  .rank-img-wrap.is-tilting .rank-img-3d{ transition:transform 80ms linear; }
  .rank-gloss{
    /* The reflection is masked by the icon's own alpha channel (the mask image
       is set in JS from the <img> src) so the highlight only ever falls on the
       badge itself — never on the transparent square around it. */
    position:absolute; inset:0; pointer-events:none;
    opacity:0; transition:opacity 320ms ease;
    mask-repeat:no-repeat; mask-position:center; mask-size:contain;
    -webkit-mask-repeat:no-repeat; -webkit-mask-position:center; -webkit-mask-size:contain;
    mix-blend-mode:screen;
  }
  .rank-img-wrap.is-tilting .rank-gloss{ opacity:1; }
  .rank-gloss::before{
    content:""; position:absolute;
    left:var(--gx,50%); top:var(--gy,50%);
    width:118px; height:118px; border-radius:50%;
    transform:translate(-50%,-50%);
    background:radial-gradient(circle at center,
      rgba(255,255,255,0.92) 0%,
      rgba(255,226,232,0.50) 38%,
      rgba(255,150,170,0.16) 62%,
      rgba(255,255,255,0) 74%);
    filter:blur(15px);
    will-change:left, top;
  }
  @media (prefers-reduced-motion: reduce){
    .rank-img-3d, .rank-gloss{ transition:none; }
  }
  .rank-name{
    font-size:24px; font-weight:800; letter-spacing:.01em;
    color:var(--rank-name);
    text-shadow:0 0 18px rgba(var(--rank-main),0.35);
    margin-top:6px;
  }
  /* Admin: the rank name becomes a button that opens a colour-coded chooser. */
  .rank-name.is-editable{ cursor:pointer; border-bottom:1px dashed rgba(var(--rank-main),0.55); }
  .rank-name.is-editable:hover{ opacity:.85; }
  .rank-picker{
    position:absolute; left:50%; top:calc(100% + 10px); transform:translateX(-50%);
    z-index:40; width:min(440px, calc(100vw - 32px));
    background:var(--bg-surface); border:1px solid var(--border); border-radius:14px;
    box-shadow:0 18px 40px rgba(0,0,0,0.35); padding:14px; text-align:left;
  }
  .rank-picker-head{
    font-family:var(--font-mono); font-size:10.5px; font-weight:700; letter-spacing:.16em;
    text-transform:uppercase; color:var(--text-tertiary); margin-bottom:10px;
  }
  .rank-picker-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:6px; }
  .rank-opt{
    display:flex; align-items:center; gap:8px; padding:7px 10px; border-radius:8px;
    border:1px solid var(--border); background:var(--bg-surface-2); cursor:pointer;
    color:var(--text-secondary); font-size:12.5px; font-family:inherit; text-align:left;
  }
  .rank-opt:hover{ border-color:var(--accent-soft-2); color:var(--text-primary); }
  .rank-opt.selected{ border-color:var(--accent); background:var(--accent-soft); color:var(--text-primary); }
  .rank-opt-dot{ width:11px; height:11px; border-radius:50%; background:var(--opt); flex-shrink:0; }

  .rank-sub{
    font-size:12.5px; color:var(--text-tertiary);
    font-style:italic;
  }

  /* ---------- dashboard ---------- */
  /* Completion gets the wider half — it's the number people come back for.
     The continue card is deliberately lean: eyebrow (with the tier folded in),
     the mechanic name, and the action. Nothing else. */
  .dash-grid{display:grid; grid-template-columns:1fr 1.25fr; gap:16px; margin-bottom:16px;}
  .continue-card{display:flex; flex-direction:column; gap:12px; align-items:flex-start;}
  .continue-eyebrow{
    display:flex; align-items:center; gap:7px;
    font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--accent);
  }
  .continue-eyebrow-sep{color:var(--text-tertiary); font-weight:400;}
  .continue-eyebrow-tier{color:var(--text-tertiary); letter-spacing:.06em;}
  .continue-name{font-size:22px; font-weight:800; letter-spacing:-0.01em; line-height:1.2;}
  .continue-card .btn{margin-top:auto;}
  .continue-desc{color:var(--text-secondary); font-size:13px;}

  /* ---------- mechanic goal card (replaces Continue learning when set) ---------- */
  .goal-head{display:flex; justify-content:space-between; align-items:flex-start; width:100%; gap:10px;}
  .goal-eyebrow{color:var(--goal);}
  .goal-dot{
    display:inline-block; width:6px; height:6px; border-radius:50%;
    background:var(--goal); margin-right:7px; vertical-align:1px;
    box-shadow:0 0 8px var(--goal-soft);
  }
  .goal-clear{padding:4px 10px; font-size:11.5px; border-radius:7px; cursor:pointer; font-family:inherit;}
  .goal-bar{height:7px; width:100%; border-radius:999px; background:var(--bg-surface-3); overflow:hidden;}
  .goal-count{
    margin-left:8px; padding:1px 6px; border-radius:999px;
    background:var(--goal-soft); color:var(--goal);
    font-family:var(--font-mono); font-size:10px; font-weight:700; letter-spacing:.02em;
  }
  .goal-next-link{
    display:flex; align-items:center; gap:8px; width:100%; margin-top:auto;
    padding:9px 11px; border-radius:9px; cursor:pointer; font-family:inherit;
    background:var(--bg-surface-2); border:1px solid var(--border-soft);
    color:var(--text-primary); text-align:left;
  }
  .goal-next-link:hover{border-color:var(--goal); color:var(--goal);}
  .goal-next-link svg{width:15px; height:15px; margin-left:auto; flex-shrink:0;}
  .goal-next-label{
    font-family:var(--font-mono); font-size:9.5px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase; color:var(--text-tertiary); flex-shrink:0;
  }
  .goal-next-name{
    font-size:13.5px; font-weight:700;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .goal-bar-fill{height:100%; border-radius:999px; background:var(--goal); transition:width .5s cubic-bezier(.4,0,.2,1);}
  .goal-actions{margin-top:auto;}
  .goal-btn{
    display:inline-flex; align-items:center; gap:7px;
    padding:8px 14px; border-radius:9px; font-size:13px; font-weight:600;
    cursor:pointer; font-family:inherit;
  }
  .goal-btn:hover{border-color:var(--goal);}
  .goal-btn.is-goal{border-color:var(--goal); color:var(--goal); background:var(--goal-soft);}
  .goal-btn-dot{width:7px; height:7px; border-radius:50%; background:var(--goal); flex-shrink:0;}
  .btn{
    display:inline-flex; align-items:center; justify-content:center; gap:7px;
    background:var(--accent); color:var(--on-accent); border:none;
    padding:9px 16px; border-radius:var(--radius-sm);
    font-size:13.5px; font-weight:600; cursor:pointer;
  }
  .btn:hover{filter:brightness(1.08);}
  .btn-ghost{background:none; border:1px solid var(--border); color:var(--text-primary);}
  .btn-ghost:hover{border-color:var(--accent-soft-2); background:var(--bg-surface-2);}
  .btn svg{width:14px;height:14px;}

  .stat-card{display:flex; flex-direction:column; gap:14px; justify-content:center;}
  .stat-label{font-size:12px; color:var(--text-secondary); font-weight:600;}
  .stat-label-lg{font-size:19px; font-weight:700; color:var(--text-primary);}

  /* Completion: title and figure share one row, the bar spans the full width
     beneath them, and a single quiet caption sits under that. The old version
     had the number, the caption and a chunky segmented meter all competing —
     this gives each piece one job and one place. */
  .completion-head{display:flex; align-items:baseline; justify-content:space-between; gap:12px;}
  .completion-pct{font-size:30px; font-weight:800; letter-spacing:-0.02em; color:var(--text-primary); line-height:1;}
  .completion-pct-sym{font-size:16px; font-weight:700; color:var(--text-tertiary); margin-left:1px;}
  .completion-bar{
    height:8px; width:100%; border-radius:999px;
    background:var(--bg-surface-3); overflow:hidden;
  }
  .completion-bar-fill{
    height:100%; border-radius:999px; background:var(--success);
    transition:width .5s cubic-bezier(.4,0,.2,1);
  }
  .completion-sub{font-size:13px; color:var(--text-secondary);}

  .nav-cards{display:grid; grid-template-columns:repeat(3,1fr); gap:14px; margin-bottom:28px;}
  .nav-card{display:flex; flex-direction:column; gap:10px;}
  .nav-card-icon{
    width:34px;height:34px; border-radius:8px; background:var(--accent-soft);
    color:var(--accent); display:flex; align-items:center; justify-content:center;
  }
  .nav-card-icon svg{width:18px;height:18px;}
  .nav-card-title{font-weight:700; font-size:14.5px;}
  .nav-card-count{font-size:12px; color:var(--text-tertiary);}

  .recent-list{display:flex; flex-direction:column; gap:2px;}
  .recent-row{
    display:flex; align-items:center; justify-content:space-between; gap:10px;
    padding:10px 12px; border-radius:var(--radius-sm); cursor:pointer;
  }
  .recent-row:hover{background:var(--bg-surface-2);}
  .recent-row-left{display:flex; align-items:center; gap:10px; min-width:0;}
  .recent-kind{font-family:var(--font-mono); font-size:10px; color:var(--text-tertiary); text-transform:uppercase;}
  .recent-name{font-size:13.5px; font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .empty-note{color:var(--text-tertiary); font-size:13px; padding:14px 12px;}

  /* ---------- list pages ---------- */
  .tier-group{margin-bottom:26px;}
  .tier-group-head{display:flex; align-items:center; gap:10px; margin-bottom:10px;}
  .tier-group-line{flex:1; height:1px; background:var(--border-soft);}

  .item-row{
    display:flex; align-items:center; gap:14px;
    padding:14px 16px; border-radius:var(--radius); border:1px solid var(--border-soft);
    background:var(--bg-surface); margin-bottom:8px; cursor:pointer;
  }
  .item-row:hover{border-color:var(--border);}
  .item-check{
    width:20px;height:20px; border-radius:5px; border:1.5px solid var(--border);
    flex-shrink:0; display:flex; align-items:center; justify-content:center;
    cursor:pointer; background:var(--bg-surface-2);
  }
  .item-check.checked{background:var(--success-soft); border-color:var(--success); color:var(--success);}
  /* The checkbox uses Icon.checkTick — a check whose viewBox is cropped tight
     to the mark — so the glyph fills the box at its natural size instead of
     floating small inside baked-in padding. The svg carries explicit width/height
     ATTRIBUTES (not just CSS) because Safari/iOS otherwise mis-sizes a viewBox-only
     inline svg inside a flex box and renders the tick tiny. flex-shrink:0 stops the
     flex box squashing it. Scoped to .item-check; shared Icon.check is untouched. */
  .item-check svg{width:14.4px;height:14.4px; min-width:14.4px; opacity:0; flex-shrink:0;}
  .item-check.checked svg{opacity:1;}
  .item-main{flex:1; min-width:0;}
  .item-name{font-weight:600; font-size:14.5px; margin-bottom:3px;}
  .item-meta{display:flex; align-items:center; gap:8px; font-size:12px; color:var(--text-tertiary);}
  .item-chevron{width:16px;height:16px; color:var(--text-tertiary); flex-shrink:0;}

  .physics-row{display:flex; align-items:center; gap:14px; padding:14px 16px; border-radius:var(--radius); border:1px solid var(--border-soft); background:var(--bg-surface); margin-bottom:8px; cursor:pointer;}
  .physics-row:hover{border-color:var(--border);}
  .physics-icon{width:30px;height:30px; border-radius:7px; background:var(--bg-surface-3); display:flex; align-items:center; justify-content:center; flex-shrink:0; color:var(--text-secondary);}
  .physics-icon svg{width:15px;height:15px;}

  /* ---------- view toggle (list / grid) ---------- */
  .right-controls{display:flex; align-items:center; gap:10px; flex-shrink:0;}
  .view-toggle{display:flex; gap:2px; background:var(--bg-surface-2); border:1px solid var(--border); border-radius:7px; padding:2px;}
  .view-toggle-btn{width:28px;height:26px; border-radius:5px; border:none; background:none; color:var(--text-tertiary); display:flex; align-items:center; justify-content:center; cursor:pointer;}
  .view-toggle-btn svg{width:14px;height:14px;}
  .view-toggle-btn.active{background:var(--bg-surface-3); color:var(--accent);}

  /* ---------- card / grid views ---------- */
  .card-grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(200px,1fr)); gap:12px; margin-bottom:8px;}
  .mech-card, .physics-card{
    background:var(--bg-surface); border:1px solid var(--border-soft); border-radius:var(--radius-lg);
    padding:16px; cursor:pointer; display:flex; flex-direction:column; gap:10px; position:relative;
  }
  .mech-card:hover, .physics-card:hover{border-color:var(--border);}
  /* Admin content marker on the list surfaces: a violet stripe on rows and a
     violet ring on cards, plus a faint tint. box-shadow (not border) keeps
     layout stable and avoids fighting the cards' hover border change. */
  .item-row.has-content, .physics-row.has-content{background:var(--content-soft); box-shadow:inset 3px 0 0 var(--content);}
  .mech-card.has-content, .physics-card.has-content{background:var(--content-soft); box-shadow:inset 0 0 0 1.5px var(--content);}
  .mech-card-top{display:flex; align-items:flex-start; justify-content:space-between; gap:8px;}
  .mech-card-name{font-weight:700; font-size:14.5px; line-height:1.35; flex:1; min-width:0;}
  .mech-card-meta{font-size:12px; color:var(--text-tertiary); display:flex; flex-direction:column; gap:2px;}
  /* Physics card: bare type icon (no boxed background) on a row with the type
     label, e.g. an atom next to "Physics". The row matches the related-mechanics
     line in size/font/colour (icon inherits that colour), and sits closer to the
     count below it than to the title above it. (List-row icon keeps its box.) */
  .physics-type{display:flex; align-items:center; gap:6px; font-size:12px; color:var(--text-tertiary); margin-top:6px; margin-bottom:-5px;}
  .physics-type-icon{display:inline-flex; color:inherit;}
  .physics-type-icon svg{width:15px; height:15px;}
  /* Admin edit/reorder cluster sits bottom-right on cards now — the top-right
     corner belongs to the completion checkbox in the new card layout. */
  .card-admin-actions{position:absolute; top:auto; bottom:10px; right:10px; display:flex; gap:4px;}

  /* ---------- detail header ---------- */
  .detail-header{margin-bottom:22px;}
  .detail-eyebrow{display:flex; align-items:center; gap:8px; margin-bottom:10px;}
  .back-link{display:inline-flex; align-items:center; gap:5px; color:var(--text-tertiary); font-size:12.5px; font-weight:600; cursor:pointer;}
  .back-link:hover{color:var(--text-secondary);}
  .back-link svg{width:13px;height:13px;}
  .detail-title-row{display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap;}
  .detail-title{font-size:25px; font-weight:800; margin:0;}
  .detail-admin-actions{display:flex; align-items:center; gap:10px; flex-wrap:wrap;}
  .complete-toggle{
    display:flex; align-items:center; gap:9px;
    padding:8px 14px; border-radius:999px; border:1px solid var(--border);
    background:var(--bg-surface); cursor:pointer; font-size:13px; font-weight:600;
    color:var(--text-secondary); flex-shrink:0;
  }
  .complete-toggle.done{border-color:var(--success); color:var(--success); background:var(--success-soft);}
  .toggle-track{width:30px; height:17px; border-radius:999px; background:var(--bg-surface-3); position:relative; flex-shrink:0;}
  .toggle-knob{width:13px;height:13px; border-radius:50%; background:var(--text-tertiary); position:absolute; top:2px; left:2px;}
  .complete-toggle.done .toggle-track{background:var(--success-soft);}
  .complete-toggle.done .toggle-knob{background:var(--success); left:15px;}

  .prereq-row{display:flex; gap:28px; flex-wrap:wrap; margin:18px 0 18px; padding:16px; background:var(--bg-surface); border:1px solid var(--border-soft); border-radius:var(--radius-lg);}
  .prereq-col{display:flex; flex-direction:column; gap:8px; min-width:140px;}
  /* Tier is intrinsic to the mechanic; the columns after it are relationships
     to other content. A hairline separates the two kinds of information. */
  .prereq-col:first-child{
    min-width:0; padding-right:28px;
    border-right:1px solid var(--border-soft);
  }
  .prereq-label{font-size:10.5px; font-weight:700; letter-spacing:.07em; text-transform:uppercase; color:var(--text-tertiary);}
  .prereq-chips{display:flex; flex-wrap:wrap; gap:6px;}
  .prereq-none{font-size:12.5px; color:var(--text-tertiary);}

  /* Expand / collapse every section on the page at once. */
  .sections-toolbar{display:flex; justify-content:flex-end; margin-bottom:10px;}
  .expand-all-btn{
    display:inline-flex; align-items:center; gap:6px;
    background:none; border:1px solid var(--border-soft); border-radius:var(--radius-sm);
    padding:6px 11px; cursor:pointer;
    font-size:12px; font-weight:600; color:var(--text-tertiary);
  }
  .expand-all-btn:hover{color:var(--text-primary); border-color:var(--accent-soft-2);}
  .expand-all-btn svg{width:14px; height:14px;}

  /* ---------- sections / tiles ---------- */
  .section-tile{border:1px solid var(--border-soft); border-radius:var(--radius); margin-bottom:10px; overflow:hidden; background:var(--bg-surface);}
  .section-tile.nested{background:var(--bg-surface-2); margin:14px 0 0;}
  .section-head{
    display:flex; align-items:center; justify-content:between; gap:10px;
    padding:13px 16px; cursor:pointer; width:100%; background:none; border:none;
    text-align:left; justify-content:space-between;
  }
  .section-head:hover{background:var(--bg-surface-2);}
  .section-title{font-weight:700; font-size:14px;}
  .section-count{display:inline-block; margin-left:6px; padding:1px 7px; border-radius:999px; font-family:var(--font-mono); font-size:10.5px; font-weight:700; background:var(--bg-surface-3); color:var(--text-secondary); border:1px solid var(--border); vertical-align:1px;}
  .section-chevron{width:15px;height:15px; color:var(--text-tertiary); transition:transform .15s ease; flex-shrink:0;}
  .section-tile.open .section-chevron{transform:rotate(180deg);}
  .section-body{padding:4px 16px 16px;}
  .section-body.hidden{display:none;}

  .tile-paragraph{margin:0 0 12px; color:var(--text-primary); font-size:14px; line-height:1.65;}
  .tile-paragraph strong, .tile-heading strong, .tile-quote strong,
  .tile-callout strong, .tile-compare p strong{ font-weight:700; color:var(--text-primary); }
  .tile-paragraph em, .tile-heading em, .tile-quote em,
  .tile-callout em, .tile-compare p em{ font-style:italic; }
  .richtext-hint{
    margin-top:6px; font-size:11px; color:var(--text-tertiary,rgba(255,255,255,0.38));
    font-family:var(--font-mono); letter-spacing:.02em;
  }
  /* interactive demo tiles */
  .tile-demo{ margin:18px 0; padding:16px; border-radius:14px; background:var(--surface-1,#1d222b); border:1px solid var(--border,rgba(255,255,255,0.08)); }
  .tile-demo-title{ font-size:14px; font-weight:700; margin-bottom:12px; color:var(--text-primary); }
  .tile-demo-mount{ display:flex; flex-direction:column; align-items:center; }
  .normals-svg{ max-width:560px; cursor:grab; user-select:none; }
  .coming-soon{
    display:flex; flex-direction:column; align-items:center; text-align:center;
    padding:56px 24px; margin-top:8px;
    background:var(--surface-1,#1d222b);
    border:1px dashed var(--border-strong,rgba(255,255,255,0.16));
    border-radius:16px;
  }
  .coming-soon-icon{
    display:flex; align-items:center; justify-content:center;
    width:52px; height:52px; border-radius:14px; margin-bottom:16px;
    background:var(--accent-soft, rgba(255,196,0,0.12));
    color:var(--accent, #ffc400);
  }
  .coming-soon-icon svg{ width:28px; height:28px; }
  .coming-soon-title{ font-size:19px; font-weight:800; color:var(--text-primary); letter-spacing:.01em; }
  .coming-soon-text{ margin-top:6px; font-size:14px; color:var(--text-tertiary,rgba(255,255,255,0.5)); max-width:340px; line-height:1.5; }
  .normals-svg:active{ cursor:grabbing; }
  .nm-box{ fill:rgba(120,150,255,0.05); stroke:var(--text-tertiary,rgba(255,255,255,0.35)); stroke-width:2; }
  .nm-circle{ fill:none; stroke:rgba(120,170,255,0.55); stroke-width:1.6; stroke-dasharray:5 5; }
  .nm-com{ fill:var(--text-primary); }
  .nm-label{ fill:var(--text-tertiary,rgba(255,255,255,0.5)); font-size:11px; font-family:var(--font-mono); }
  .nm-ray{ stroke:rgba(255,255,255,0.4); stroke-width:1.4; }
  .nm-tangent{ stroke:rgba(120,170,255,0.4); stroke-width:1.4; }
  .nm-ghost{ stroke:rgba(255,255,255,0.32); stroke-width:2; stroke-dasharray:4 3; }
  .nm-ghost-head{ fill:rgba(255,255,255,0.32); }
  .nm-normal{ stroke:var(--accent,#ffc400); stroke-width:3; }
  .nm-normal-head{ fill:var(--accent,#ffc400); }
  .nm-dot-circle{ fill:rgba(120,170,255,0.9); }
  .nm-dot-hit{ fill:var(--accent,#ffc400); }
  .nm-handle{ fill:rgba(255,196,0,0.001); stroke:var(--accent,#ffc400); stroke-width:1.5; stroke-opacity:0.5; }
  .nm-legend{ display:flex; flex-wrap:wrap; gap:14px; justify-content:center; margin-top:14px; }
  .nm-key{ display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--text-secondary); }
  .nm-sw{ width:16px; height:3px; border-radius:2px; display:inline-block; }
  .sw-normal{ background:var(--accent,#ffc400); height:3px; }
  .sw-ghost{ background:rgba(255,255,255,0.4); height:0; border-top:2px dashed rgba(255,255,255,0.5); }
  .sw-circle{ background:transparent; border-top:2px dashed rgba(120,170,255,0.6); }
  .nm-readout{ margin-top:12px; font-size:13px; font-weight:600; color:var(--text-primary); text-align:center; min-height:18px; }
  .nm-hint{ margin-top:5px; font-size:11px; color:var(--text-tertiary,rgba(255,255,255,0.42)); font-family:var(--font-mono); text-align:center; }

  /* ---------- deadzone visualiser (demo: rl-deadzone) ---------- */
  /* Placeholder settings row — visibly present but inert, so the feature reads
     as planned rather than broken. */
  .acct-setting-row.is-disabled{ opacity:.55; }
  .acct-setting-row.is-disabled .bool-toggle{ cursor:not-allowed; }
  .acct-soon{
    display:inline-block; margin-left:8px; padding:2px 7px; border-radius:999px;
    background:var(--bg-surface-3); color:var(--text-tertiary);
    font-size:10px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
    vertical-align:middle;
  }

  .dz-wrap{ width:100%; max-width:780px; display:flex; flex-direction:column; gap:14px; }
  .dz-controls{ display:flex; align-items:center; gap:16px; flex-wrap:wrap; }
  .dz-seg{ display:flex; border:1px solid var(--border); border-radius:8px; overflow:hidden; flex-shrink:0; }
  .dz-seg button{ padding:7px 15px; font-size:12.5px; font-weight:600; background:var(--bg-surface-2); color:var(--text-secondary); border:none; cursor:pointer; font-family:inherit; }
  .dz-seg button + button{ border-left:1px solid var(--border); }
  .dz-seg button:hover{ color:var(--text-primary); }
  .dz-seg button.active{ background:var(--accent-soft); color:var(--accent); }
  .dz-slider-row{ display:flex; align-items:center; gap:10px; flex:1 1 220px; min-width:190px; }
  .dz-slider-row label{ font-size:12.5px; font-weight:600; color:var(--text-secondary); white-space:nowrap; }
  .dz-slider-row input[type=range]{ flex:1; min-width:80px; accent-color:var(--accent); cursor:pointer; }
  .dz-val{ font-family:var(--font-mono); font-size:12px; color:var(--text-primary); min-width:44px; text-align:right; }
  .dz-panels{ display:flex; gap:16px; flex-wrap:wrap; }
  .dz-panel{ flex:1 1 165px; min-width:150px; }
  .dz-panel-title{ display:flex; align-items:center; gap:6px; font-size:10.5px; letter-spacing:.07em; text-transform:uppercase; font-weight:700; color:var(--text-tertiary); margin-bottom:7px; }
  /* Step number — makes the three panels read as a pipeline rather than three
     unrelated diagrams. */
  .dz-step{ display:inline-flex; align-items:center; justify-content:center; width:15px; height:15px; border-radius:50%; background:var(--bg-surface-3); color:var(--text-secondary); font-size:9px; letter-spacing:0; flex-shrink:0; }
  .dz-svg{ width:100%; height:auto; display:block; touch-action:none; border-radius:10px; }
  .dz-svg.is-input{ cursor:crosshair; }
  .dz-readout{ font-family:var(--font-mono); font-size:11px; color:var(--text-tertiary); margin-top:7px; }
  .dz-frame{ fill:var(--bg-surface-2); stroke:var(--border); stroke-width:1.2; }
  .dz-region{ fill:rgba(79,140,255,0.07); stroke:rgba(120,170,255,0.55); stroke-width:1.5; }
  .dz-band{ fill:rgba(240,90,110,0.17); }
  /* Panel 3's region is always the full square; the dashed path shows what the
     stick can actually reach, so the gap between them is the unreachable area. */
  .dz-square{ fill:rgba(79,140,255,0.05); }
  .dz-reach{ fill:none; stroke:rgba(120,170,255,0.75); stroke-width:1.5; stroke-dasharray:4 4; }
  .dz-axis{ stroke:var(--border); stroke-width:1; stroke-dasharray:3 4; }
  .dz-vec{ stroke:var(--accent); stroke-width:1.6; opacity:0.55; }
  .dz-vec-out{ stroke:#34d399; stroke-width:1.6; opacity:0.55; }
  .dz-dot{ fill:var(--accent); stroke:var(--bg-base); stroke-width:1.6; }
  .is-stage3 .dz-dot{ fill:#34d399; }
  .is-stage3 .dz-vec{ stroke:#34d399; }
  .dz-dot-out{ fill:#34d399; stroke:var(--bg-base); stroke-width:1.6; }
  .dz-hint{ font-size:11.5px; color:var(--text-tertiary); }
  .dz-foot{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
  .dz-tip{ position:relative; display:inline-flex; }
  /* Tooltip lives on the wrapper, not the button — disabled buttons don't
     reliably fire hover events, so the wrapper carries the :hover. */
  .dz-tip::after{
    content:attr(data-tip); position:absolute; bottom:calc(100% + 8px); left:50%;
    transform:translateX(-50%); background:var(--bg-surface-3); color:var(--text-primary);
    border:1px solid var(--border); font-size:11.5px; padding:5px 9px; border-radius:6px;
    white-space:nowrap; opacity:0; pointer-events:none; transition:opacity .15s ease;
  }
  .dz-tip:hover::after{ opacity:1; }
  .dz-connect{
    padding:9px 15px; border-radius:9px; border:1px solid var(--border);
    background:var(--bg-surface-2); color:var(--text-tertiary);
    font-size:13px; font-weight:600; font-family:inherit; cursor:not-allowed; opacity:.55;
  }
  /* Glossary terms in body copy: subtle dotted underline, pointer to invite
     hover/tap. The popover is a single reused element repositioned per term. */
  .gloss-term{
    text-decoration:underline dotted;
    text-decoration-color:var(--accent-soft-2, rgba(255,196,0,0.55));
    text-underline-offset:3px;
    cursor:help; outline:none;
  }
  .gloss-term:hover, .gloss-term:focus{ text-decoration-style:solid; }
  .gloss-pop{
    position:fixed; z-index:300; max-width:300px;
    background:var(--surface-1,#1d222b); border:1px solid var(--border-strong,rgba(255,255,255,0.14));
    border-radius:10px; padding:11px 13px; box-shadow:0 10px 30px rgba(0,0,0,0.45);
    font-size:13px; line-height:1.5; color:var(--text-primary);
    animation:glossPopIn .14s ease-out both;
  }
  .gloss-pop-term{ font-weight:700; margin-bottom:4px; }
  .gloss-pop-alias{ font-size:11px; color:var(--text-tertiary,rgba(255,255,255,0.4)); margin-bottom:6px; }
  .gloss-pop img{ max-width:100%; border-radius:6px; margin-top:8px; display:block; }
  @keyframes glossPopIn{ from{opacity:0; transform:translateY(3px);} to{opacity:1; transform:none;} }
  @media (prefers-reduced-motion: reduce){ .gloss-pop{ animation:none; } }
  body.light-mode .gloss-pop{ background:#fff; box-shadow:0 10px 30px rgba(0,0,0,0.18); }
  /* Glossary launcher: a small pill fixed to the bottom-center of the screen,
     always visible while on the Physics & Theory tab (not just when you scroll
     to the end of the list). Fixed to the viewport, but inset from the left by
     the sidebar width so its center lines up with the content column rather
     than the whole window. The gradient fades list rows out behind it. */
  .glossary-bottom{
    position:fixed; z-index:40;
    left:var(--sidebar-w); right:0; bottom:0;
    display:flex; justify-content:center;
    padding:34px 0 16px;
    pointer-events:none;   /* only the button is interactive; gradient lets clicks through */
    background:linear-gradient(to bottom,
      transparent 0%,
      var(--bg-base, #12141a) 62%,
      var(--bg-base, #12141a) 100%);
  }
  /* Give the list room so its last rows can scroll clear of the fixed pill
     instead of hiding permanently behind it. */
  .physics-has-glossary-pad{ padding-bottom:64px; }
  .glossary-btn{
    pointer-events:auto;
    display:inline-flex; align-items:center; gap:6px;
    padding:8px 12px 8px 16px; border-radius:999px; cursor:pointer;
    background:var(--surface-1,#1d222b);
    border:1px solid var(--border,rgba(255,255,255,0.09));
    color:var(--text-primary);
    font-size:13px; font-weight:700;
    transition:border-color .16s, background .16s, color .16s;
  }
  .glossary-btn:hover{
    border-color:var(--accent, #ffc400);
    background:var(--surface-2,#22272f);
  }
  .glossary-btn .gl-cta{ display:inline-flex; color:var(--text-tertiary,rgba(255,255,255,0.5)); }
  .glossary-btn .gl-cta svg{ width:15px; height:15px; }
  .glossary-btn:hover .gl-cta{ color:var(--accent,#ffc400); }
  body.light-mode .glossary-btn{ background:#f4f5f7; }
  body.light-mode .glossary-btn:hover{ background:#eef0f3; }
  /* Glossary page */
  .gloss-entry{ padding:16px 0; border-bottom:1px solid var(--border,rgba(255,255,255,0.08)); }
  .gloss-entry:last-child{ border-bottom:none; }
  .gloss-entry-term{ font-size:16px; font-weight:700; color:var(--text-primary); }
  .gloss-entry-alias{ font-size:12px; color:var(--text-tertiary,rgba(255,255,255,0.4)); margin-top:2px; }
  .gloss-entry-body{ margin-top:7px; font-size:14px; line-height:1.6; color:var(--text-secondary); }
  .gloss-entry img{ max-width:340px; width:100%; border-radius:8px; margin-top:10px; display:block; }
  .gloss-entry-actions{ display:flex; gap:6px; margin-top:8px; }
  /* Wizard glossary-linking step */
  .gloss-linker-tile{ margin-bottom:14px; }
  .gloss-linker-label{ font-size:11px; font-family:var(--font-mono); color:var(--text-tertiary,rgba(255,255,255,0.4)); margin-bottom:5px; letter-spacing:.04em; }
  .gloss-linker-text{ font-size:14px; line-height:1.7; color:var(--text-primary); background:var(--surface-2,rgba(255,255,255,0.03)); border:1px solid var(--border,rgba(255,255,255,0.08)); border-radius:8px; padding:10px 13px; white-space:pre-wrap; }
  .gloss-hit{ border-radius:4px; padding:1px 3px; cursor:pointer; }
  .gloss-hit.unsel{ background:rgba(255,196,0,0.22); box-shadow:inset 0 0 0 1px rgba(255,196,0,0.4); }
  .gloss-hit.unsel:hover{ background:rgba(255,196,0,0.34); }
  .gloss-hit.sel{ background:rgba(80,200,120,0.25); box-shadow:inset 0 0 0 1px rgba(80,200,120,0.5); }
  .gloss-hit.sel:hover{ background:rgba(80,200,120,0.38); }
  .tile-heading{font-weight:700; margin:18px 0 8px; font-size:15px;}
  .tile-heading:first-child{margin-top:2px;}
  .tile-divider{height:1px; background:var(--border-soft); margin:14px 0; border:none;}
  .tile-spacer{height:18px;}

  .tile-callout{display:flex; gap:10px; padding:12px 14px; border-radius:var(--radius); margin:0 0 12px; font-size:13.5px; line-height:1.55;}
  .tile-callout svg{width:16px;height:16px; flex-shrink:0; margin-top:1px;}
  .tile-tip{background:var(--accent-soft); color:#cfe1ff;}
  .tile-tip svg{color:var(--accent);}
  .tile-warning{background:var(--warning-soft); color:#ffe1bf;}
  .tile-warning svg{color:var(--warning);}
  .tile-quote{border-left:2px solid var(--border); padding:4px 0 4px 14px; margin:0 0 14px; font-style:italic; color:var(--text-secondary); font-size:14px;}
  .tile-quote-attr{display:block; font-style:normal; font-size:12px; color:var(--text-tertiary); margin-top:5px;}

  .tile-image{margin:0 0 14px;}
  .tile-image-frame{background:var(--bg-surface-2); border:1px solid var(--border-soft); border-radius:var(--radius); display:flex; align-items:center; justify-content:center; padding:18px;}
  .tile-image-frame svg{max-width:100%; height:auto;}
  .tile-caption{font-size:12px; color:var(--text-tertiary); margin-top:7px; text-align:center;}

  /* ---------- table tile (reader) ---------- */
  .tile-table-wrap{margin:0 0 16px; overflow-x:auto;}
  .tile-table{
    width:100%; border-collapse:collapse; font-size:13.5px;
    border:1px solid var(--border-soft); border-radius:var(--radius); overflow:hidden;
  }
  .tile-table th{
    text-align:left; padding:10px 13px; background:var(--bg-surface-2);
    font-weight:700; font-size:12px; letter-spacing:.04em; text-transform:uppercase;
    color:var(--text-secondary); border-bottom:1px solid var(--border);
    white-space:nowrap;
  }
  .tile-table td{
    padding:10px 13px; border-bottom:1px solid var(--border-soft);
    color:var(--text-primary); vertical-align:top;
  }
  .tile-table tbody tr:last-child td{border-bottom:none;}
  .tile-table tbody tr:hover td{background:var(--bg-surface-2);}

  /* ---------- do / don't comparison tile (reader) ---------- */
  .tile-compare{display:grid; grid-template-columns:1fr 1fr; gap:12px; margin:0 0 16px;}
  .tile-compare-col{
    padding:14px; border-radius:var(--radius);
    border:1px solid var(--border-soft); background:var(--bg-surface-2);
  }
  .tile-compare-col p{margin:0; font-size:13.5px; line-height:1.55; color:var(--text-secondary);}
  .tile-compare-head{
    display:flex; align-items:center; gap:7px; margin-bottom:8px;
    font-size:12.5px; font-weight:700; letter-spacing:.03em; text-transform:uppercase;
  }
  .tile-compare-head svg{width:15px; height:15px;}
  .tile-compare-col.is-good{border-color:rgba(46,160,102,0.35); background:rgba(46,160,102,0.07);}
  .tile-compare-col.is-good .tile-compare-head{color:var(--success);}
  .tile-compare-col.is-bad{border-color:rgba(229,72,77,0.35); background:rgba(229,72,77,0.07);}
  .tile-compare-col.is-bad .tile-compare-head{color:#e5484d;}
  @media (max-width:640px){ .tile-compare{grid-template-columns:1fr;} }

  /* ---------- table & comparison editors ---------- */
  .tbl-editor{display:flex; flex-direction:column; gap:8px;}
  .tbl-scroll{overflow-x:auto;}
  .tbl-edit-table{border-collapse:collapse; width:100%;}
  .tbl-edit-table th, .tbl-edit-table td{padding:0; border:1px solid var(--border-soft);}
  .tbl-edit-table th.tbl-rowctl, .tbl-edit-table td.tbl-rowctl{border:none; width:34px; text-align:center;}
  .tbl-cell{
    width:100%; min-width:110px; box-sizing:border-box; border:none; outline:none;
    background:var(--bg-surface-2); color:var(--text-primary);
    padding:8px 10px; font-size:13px; font-family:inherit;
  }
  .tbl-cell:focus{background:var(--bg-surface-3); box-shadow:inset 0 0 0 1px var(--accent-soft-2);}
  .tbl-head-cell{font-weight:700; background:var(--bg-surface-3);}
  .tbl-mini-btn{
    background:none; border:none; cursor:pointer; color:var(--text-tertiary);
    padding:4px; border-radius:5px; display:inline-flex;
  }
  .tbl-mini-btn:hover{color:#e5484d; background:rgba(229,72,77,0.1);}
  .tbl-mini-btn svg{width:14px; height:14px;}
  .tbl-toolbar{display:flex; gap:6px; flex-wrap:wrap;}
  .tbl-toolbar .editor-add-btn:disabled{opacity:.4; cursor:not-allowed;}

  .cmp-editor{display:grid; grid-template-columns:1fr 1fr; gap:10px;}
  .cmp-editor-col{display:flex; flex-direction:column; gap:0;}
  .cmp-editor-col .wizard-input.small{margin-bottom:6px;}
  @media (max-width:640px){ .cmp-editor{grid-template-columns:1fr;} }

  .tile-video{margin:0 0 14px;}
  .tile-video-frame{
    background:var(--bg-surface-2); border:1px solid var(--border-soft); border-radius:var(--radius);
    aspect-ratio:16/9; display:flex; align-items:center; justify-content:center; flex-direction:column; gap:8px;
    color:var(--text-tertiary);
  }
  .tile-video-frame svg{width:30px;height:30px;}
  .tile-video-frame span{font-size:12px;}

  /* tab group */
  .tab-tile{border:1px solid var(--border-soft); border-radius:var(--radius); margin:0 0 14px; overflow:hidden;}
  .tab-tile-head{padding:13px 16px 0;}
  .tab-buttons{display:flex; gap:4px; padding:0 12px; border-bottom:1px solid var(--border-soft); margin-top:8px;}
  .tab-btn{padding:9px 14px; font-size:13px; font-weight:600; color:var(--text-tertiary); background:none; border:none; cursor:pointer; border-bottom:2px solid transparent; margin-bottom:-1px;}
  .tab-btn:hover{color:var(--text-secondary);}
  .tab-btn.active{color:var(--accent); border-color:var(--accent);}
  .tab-panel{padding:16px;}

  /* ---------- progression tree ---------- */
  .tree-wrap{
    position:relative; background:var(--bg-surface); border:1px solid var(--border-soft);
    border-radius:var(--radius-lg); overflow:hidden;
    height:min(68vh, 640px); cursor:grab; touch-action:none;
  }
  .tree-wrap.panning{cursor:grabbing;}
  .tree-canvas{position:absolute; top:0; left:0;}
  .tree-canvas svg{display:block; width:100%; height:100%;}
  /* Map-style zoom controls, bottom-right of the viewport. */
  /* ---------- tree key: colour legend + goal mode, bottom-left ----------
     Floats inside the viewport rather than sitting above it, so the tree gets
     the full height and the key stays visible while panning. Pointer events
     are stopped on the panel so dragging it doesn't pan the canvas behind. */
  /* ---------- Physics & Theory kind filter ---------- */
  .physics-filter-row{display:flex; gap:7px; flex-wrap:wrap; margin:-4px 0 16px;}
  .pf-chip{
    display:inline-flex; align-items:center; gap:6px;
    padding:6px 11px; border-radius:999px; cursor:pointer; font-family:inherit;
    background:var(--bg-surface-2); border:1px solid var(--border);
    color:var(--text-secondary); font-size:12.5px; font-weight:600;
    transition:border-color .14s ease, color .14s ease, background .14s ease;
  }
  .pf-chip:hover{border-color:var(--accent-soft-2); color:var(--text-primary);}
  .pf-chip.selected{background:var(--accent-soft); border-color:var(--accent); color:var(--accent);}
  .pf-chip-icon{display:inline-flex;}
  .pf-chip-icon svg{width:14px; height:14px;}
  .link-btn{
    background:none; border:none; padding:0; cursor:pointer; font-family:inherit;
    font-size:inherit; color:var(--accent); text-decoration:underline;
  }

  /* ---------- pinned mechanics ---------- */
  .pin-btn{
    display:inline-flex; align-items:center; gap:7px;
    padding:8px 14px; border-radius:9px; font-size:13px; font-weight:600;
    cursor:pointer; font-family:inherit;
  }
  .pin-btn svg{width:15px; height:15px;}
  .pin-btn:hover{border-color:var(--accent);}
  .pin-btn.is-pinned{border-color:var(--accent); color:var(--accent); background:var(--accent-soft);}
  .pin-btn.is-pinned svg{fill:var(--accent-soft-2);}
  .pin-flag{display:inline-flex; margin-left:7px; color:var(--accent); vertical-align:-2px;}
  .pin-flag svg{width:13px; height:13px;}
  .pinned-group-badge{
    display:inline-flex; align-items:center; gap:6px;
    font-family:var(--font-mono); font-size:11px; font-weight:700;
    letter-spacing:.1em; text-transform:uppercase; color:var(--accent);
  }
  .pinned-group-badge svg{width:13px; height:13px;}

  /* ---------- personalised home ---------- */
  /* Deliberately quiet: present for anyone who looks, invisible to anyone who
     doesn't. Brightens to full on hover/focus. */
  .topbar-home{
    background:none; border:none; padding:6px; margin-right:2px; cursor:pointer;
    color:var(--text-tertiary); opacity:.35; display:inline-flex;
    border-radius:8px; transition:opacity .16s ease, color .16s ease, background .16s ease;
  }
  .topbar-home:hover, .topbar-home:focus-visible{
    opacity:1; color:var(--accent); background:var(--accent-soft);
  }
  .topbar-home svg{width:17px; height:17px;}

  .home-grid{
    display:grid;
    grid-template-columns:repeat(var(--home-cols), 1fr);
    grid-auto-rows:minmax(0, 1fr);
    grid-template-rows:repeat(var(--home-rows), 1fr);
    gap:12px;
    /* Cells stay square whatever the row count: the grid's ratio tracks
       cols/rows, so adding rows makes the board taller rather than squashing
       what's already on it. No viewport cap — a 12-row board is meant to
       scroll. */
    aspect-ratio:var(--home-cols) / var(--home-rows);
  }
  .home-cell{min-width:0; min-height:0;}
  .hw{
    height:100%; box-sizing:border-box; overflow:hidden;
    background:var(--bg-surface); border:1px solid var(--border);
    border-radius:var(--radius); padding:12px 14px;
    display:flex; flex-direction:column;
  }
  .hw-head{
    display:flex; align-items:center; gap:6px; margin-bottom:8px;
    font-family:var(--font-mono); font-size:10px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase; color:var(--text-tertiary);
  }
  .hw-head svg{width:12px; height:12px;}
  .hw-head-plain{
    font-family:var(--font-mono); font-size:10px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase; color:var(--text-tertiary);
  }
  .hw-empty{font-size:12px; color:var(--text-tertiary); margin:auto 0;}

  .hw-rank{align-items:center; justify-content:center; gap:6px;
    background:linear-gradient(160deg, rgba(var(--rank-deep),.30), var(--bg-surface) 70%);
    border-color:rgba(var(--rank-main),.35);}
  .hw-rank.is-tiny{padding:8px;}
  .hw-rank.is-tiny .hw-rank-badge{width:100%; max-width:none;}
  .hw-rank-badge{width:58%; max-width:96px; aspect-ratio:1; display:flex;}
  .hw-rank-badge img{width:100%; height:100%; object-fit:contain;}
  .hw-rank-name{font-size:13px; font-weight:700; color:var(--rank-name); text-align:center; line-height:1.2;}

  .hw-pin-list{display:flex; flex-direction:column; gap:4px; overflow:hidden;}
  .hw-pin{
    display:flex; align-items:center; justify-content:space-between; gap:8px;
    padding:6px 8px; border-radius:7px; border:1px solid var(--border-soft);
    background:var(--bg-surface-2); color:var(--text-primary);
    font-family:inherit; font-size:12.5px; font-weight:600; cursor:pointer; text-align:left;
  }
  .hw-pin:hover{border-color:var(--accent); color:var(--accent);}
  .hw-pin-name{overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .hw-pin-done{display:inline-flex; color:var(--success); flex-shrink:0;}
  .hw-pin-done svg{width:13px; height:13px;}

  .hw-progress{justify-content:center; gap:7px;}
  .hw-progress-top{display:flex; align-items:baseline; justify-content:space-between; gap:8px;}
  .hw-progress-pct{font-size:22px; font-weight:800; letter-spacing:-.02em;}
  .hw-progress-sym{font-size:13px; font-weight:700; color:var(--text-tertiary); margin-left:1px;}
  .hw-bar{height:6px; border-radius:999px; background:var(--bg-surface-3); overflow:hidden;}
  .hw-bar-fill{height:100%; border-radius:999px; background:var(--success); transition:width .5s cubic-bezier(.4,0,.2,1);}
  .hw-progress-sub{font-size:11.5px; color:var(--text-tertiary);}

  .home-portrait-note{display:none;}
  /* The grid's meaning depends on 8 equal columns. Below this width — or in
     portrait — a cell is too narrow to read, so the page explains itself
     instead of reflowing into something that isn't the same layout. */
  @media (max-width:860px), (orientation:portrait){
    .topbar-home{display:none;}
    .home-grid{display:none;}
    .home-portrait-note{
      display:block; padding:22px; border-radius:var(--radius);
      background:var(--bg-surface); border:1px solid var(--border);
      color:var(--text-secondary); font-size:13.5px; text-align:center;
    }
  }

  /* ---- home: toolbar, edit mode, picker ---- */
  /* clip: the iframe fills the tile at whatever shape it's been resized to */
  .hw-clip{gap:6px;}
  /* The iframe is pinned to the tile rather than sized with height:100%.
     A percentage height has to resolve through .hw (a flex column) and the
     grid cell above it; if any link in that chain is indefinite the iframe
     collapses to zero height and the embed renders invisibly. Absolute
     positioning against a relative parent has no such dependency. */
  .hw-clip.is-playing{padding:0; overflow:hidden; position:relative;}
  .hw-clip iframe{position:absolute; inset:0; width:100%; height:100%; border:0; display:block;}
  .hw-clip-status{font-size:11px; color:var(--text-tertiary);}

  .hw-username{align-items:center; justify-content:center; text-align:center;}
  /* Sized against both axes of its own tile: cqw keeps it inside the width,
     cqh stops a wide-but-short tile overflowing vertically. min() takes
     whichever limit binds first, so the name fills a 4x2 and still fits a 1x1.
     Needs container-type:size (not inline-size) for cqh to resolve. */
  .hw-username-name{
    font-size:clamp(13px, min(13cqw, 42cqh), 76px);
    font-weight:800; letter-spacing:-.015em; line-height:1.1; width:100%;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* Scoped to this tile only — applying container-type to every .home-cell put
     layout containment on all of them, which is what broke the clip embed. */
  .hw-username{container-type:size;}

  /* Decorative block: rank-themed, deliberately empty. */
  .hw-username.is-glass .hw-username-name{
    color:var(--rank-name);
    text-shadow:0 1px 10px rgba(var(--rank-main),.30);
  }
  .hw-opt{
    display:inline-flex; align-items:center; gap:6px; cursor:pointer;
    font-size:11.5px; color:var(--text-secondary); margin-top:2px;
  }
  .hw-opt input{accent-color:var(--accent); cursor:pointer;}

  /* One definition of the rank glass, shared by the decorative block and the
     username tile's optional backdrop. They were separate rules with different
     alphas and a different terminal colour, so the two drifted apart. */
  .hw-decor,
  .hw-username.is-glass{
    border-color:rgba(var(--rank-main),.28);
    background:
      radial-gradient(120% 90% at 18% 12%, rgba(var(--rank-main),.28), transparent 62%),
      linear-gradient(140deg, rgba(var(--rank-deep),.55), rgba(var(--rank-deep),.12) 55%, transparent 85%);
  }
  .hw-decor{ padding:0; position:relative; overflow:hidden; }
  .hw-decor::after{
    content:''; position:absolute; inset:0;
    background:linear-gradient(115deg, transparent 42%, rgba(var(--rank-main),.14) 50%, transparent 58%);
  }

  .hw-notes{gap:6px;}
  .hw-notes-area{
    flex:1; min-height:0; width:100%; box-sizing:border-box; resize:none;
    padding:8px 9px; border-radius:8px; line-height:1.45;
    background:var(--bg-surface-2); border:1px solid var(--border-soft);
    color:var(--text-primary); font-family:inherit; font-size:12.5px;
  }
  .hw-notes-area:focus{outline:none; border-color:var(--accent);}
  /* Notes stay typable in normal use; edit mode makes them inert like every
     other widget so a drag doesn't land in the textarea. */
  .home-cell.is-editing .hw-notes-area{pointer-events:none;}
  .home-resize{
    position:absolute; right:-3px; bottom:-3px; z-index:3;
    width:16px; height:16px; cursor:nwse-resize; border-radius:0 0 7px 0;
    background:linear-gradient(135deg, transparent 45%, var(--accent) 45%, var(--accent) 62%, transparent 62%,
                                        transparent 70%, var(--accent) 70%, var(--accent) 87%, transparent 87%);
    pointer-events:auto;
  }

  .home-toolbar{display:flex; gap:8px; align-items:center; margin-bottom:14px;}
  .home-edit-btn, .home-reset-btn{
    display:inline-flex; align-items:center; gap:7px;
    padding:7px 13px; border-radius:9px; font-size:12.5px; font-weight:600;
    cursor:pointer; font-family:inherit;
  }
  .home-edit-btn svg{width:14px; height:14px;}
  .home-edit-btn.is-on{border-color:var(--accent); color:var(--accent); background:var(--accent-soft);}

  .home-rows-step{
    display:inline-flex; align-items:center; gap:2px;
    padding:3px 4px 3px 10px; border-radius:9px;
    background:var(--bg-surface-2); border:1px solid var(--border);
  }
  .home-rows-label{
    font-family:var(--font-mono); font-size:9.5px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase; color:var(--text-tertiary); margin-right:5px;
  }
  .home-rows-step button{
    width:24px; height:24px; display:inline-flex; align-items:center; justify-content:center;
    border:none; border-radius:6px; cursor:pointer; padding:0;
    background:none; color:var(--text-secondary);
  }
  .home-rows-step button:hover:not(:disabled){background:var(--bg-surface-3); color:var(--text-primary);}
  .home-rows-step button:disabled{opacity:.3; cursor:not-allowed;}
  .home-rows-step button svg{width:13px; height:13px;}
  .home-rows-count{
    font-family:var(--font-mono); font-size:12.5px; font-weight:700;
    min-width:16px; text-align:center; color:var(--text-primary);
  }

  .home-grid.is-editing{position:relative;}
  .home-ghost{
    border:1px dashed var(--border); border-radius:8px;
    background:var(--bg-surface-2); opacity:.5; pointer-events:none;
  }
  .home-cell.is-editing{cursor:grab; position:relative;}
  .home-cell.is-editing:active{cursor:grabbing;}
  .home-cell.is-editing .hw{pointer-events:none;}
  /* Widgets go pointer-inert while arranging so a drag can't land inside them,
     but anything you configure in edit mode has to stay clickable. Matching on
     the control types rather than a hand-kept list of classes — the old version
     named .hw-quote-input only, which silently left the pack dropdown and the
     rank-glass checkbox unusable. Deliberately excludes textarea: the notes
     body stays inert so you can drag across it. */
  .home-cell.is-editing .hw input,
  .home-cell.is-editing .hw select,
  .home-cell.is-editing .hw label,
  .home-cell.is-editing .home-remove{pointer-events:auto;}
  .home-cell.drag-ok .hw{outline:2px solid var(--success); outline-offset:-1px;}
  .home-cell.drag-bad .hw{outline:2px solid var(--danger); outline-offset:-1px;}
  .home-remove{
    position:absolute; top:-7px; right:-7px; z-index:3;
    width:22px; height:22px; border-radius:50%; cursor:pointer;
    background:var(--danger); color:#fff; border:2px solid var(--bg-base);
    display:flex; align-items:center; justify-content:center; padding:0;
  }
  .home-remove svg{width:11px; height:11px;}

  .home-picker{
    margin-top:16px; padding:14px; border-radius:var(--radius);
    background:var(--bg-surface); border:1px solid var(--border);
  }
  .home-picker-head{
    font-family:var(--font-mono); font-size:10px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase; color:var(--text-tertiary); margin-bottom:10px;
  }
  .home-picker-list{display:flex; gap:8px; flex-wrap:wrap;}
  .home-picker-item{
    display:inline-flex; align-items:center; gap:8px;
    padding:8px 12px; border-radius:9px; cursor:pointer; font-family:inherit;
    background:var(--bg-surface-2); border:1px solid var(--border);
    color:var(--text-secondary); font-size:12.5px; font-weight:600;
  }
  .home-picker-item:hover{border-color:var(--accent); color:var(--accent);}
  .home-picker-size{
    font-family:var(--font-mono); font-size:10px; font-weight:700;
    padding:2px 6px; border-radius:5px;
    background:var(--bg-surface-3); color:var(--text-tertiary);
  }
  .home-picker-empty{font-size:12.5px; color:var(--text-tertiary);}

  /* ---- new widget internals ---- */
  .hw-pack-name{font-size:13.5px; font-weight:700; margin-bottom:5px;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .hw-pack-code{
    font-family:var(--font-mono); font-size:11.5px; letter-spacing:.04em;
    padding:5px 8px; border-radius:7px; cursor:pointer; text-align:left;
    background:var(--bg-surface-2); border:1px solid var(--border-soft); color:var(--text-secondary);
  }
  .hw-pack-code:hover{border-color:var(--accent); color:var(--accent);}

  .hw-quote{justify-content:center; gap:6px;}
  .hw-quote-text{font-size:13.5px; font-style:italic; line-height:1.4; color:var(--text-primary);
    overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;}
  .hw-quote-author{font-size:11.5px; color:var(--text-tertiary);}
  .hw-quote-input{
    width:100%; box-sizing:border-box; padding:6px 8px; border-radius:7px;
    background:var(--bg-surface-2); border:1px solid var(--border);
    color:var(--text-primary); font-family:inherit; font-size:12.5px;
  }
  .hw-quote-input.small{font-size:11.5px;}
  .hw-select{
    width:100%; box-sizing:border-box; padding:6px 8px; border-radius:7px;
    background:var(--bg-surface-2); border:1px solid var(--border);
    color:var(--text-primary); font-family:inherit; font-size:12.5px; cursor:pointer;
  }

  .hw-continue{justify-content:center; gap:4px;}
  .hw-continue-name{font-size:17px; font-weight:800; letter-spacing:-.01em; line-height:1.2;}
  .hw-continue-tier{font-size:11.5px; color:var(--text-tertiary); margin-bottom:8px;}
  .hw-btn{
    align-self:flex-start; display:inline-flex; align-items:center; gap:5px;
    padding:7px 12px; border-radius:8px; cursor:pointer; font-family:inherit;
    font-size:12.5px; font-weight:600;
    background:var(--accent-soft); border:1px solid var(--accent); color:var(--accent);
  }
  .hw-btn svg{width:13px; height:13px;}

  .hw-goaltree-svg{flex:1; min-height:0; display:flex;}
  .hw-goaltree-svg svg{width:100%; height:100%;}
  .hw-goaltree-sub{font-size:11px; color:var(--text-tertiary); margin-top:6px;}
  .gt-node{cursor:pointer;}
  .gt-node rect{fill:var(--bg-surface-3); stroke:var(--border); stroke-width:1;}
  .gt-node text{fill:var(--text-secondary); font-size:7px; font-family:var(--font-sans);}
  .gt-node.gt-done rect{fill:var(--tree-complete-fill); stroke:var(--success);}
  .gt-node.gt-next rect{fill:var(--goal-soft); stroke:var(--goal); stroke-width:1.8;}
  .gt-node.gt-goal rect{stroke:var(--goal); stroke-width:1.6;}
  .gt-node:hover rect{stroke:var(--accent);}

  .tree-key{
    position:absolute; left:14px; bottom:14px; z-index:5;
    display:flex; flex-direction:column; gap:9px;
    padding:11px 12px; border-radius:var(--radius);
    background:var(--bg-surface); border:1px solid var(--border);
    box-shadow:0 2px 8px rgba(0,0,0,0.18);
    max-width:min(210px, calc(100% - 28px));
  }
  .tree-key-rows{display:flex; flex-direction:column; gap:5px;}
  .tree-key-row{
    display:flex; align-items:center; gap:8px;
    font-size:11.5px; color:var(--text-secondary); white-space:nowrap;
  }
  .tree-key-chip{
    width:15px; height:11px; border-radius:var(--radius-sm); flex-shrink:0;
    background:var(--bg-surface-3); border:1.5px solid var(--border);
  }
  .tree-key-chip.is-done{background:var(--tree-complete-fill); border-color:var(--success);}
  .tree-key-chip.is-hover{background:var(--tree-hover-fill); border-color:rgb(255,196,0);}
  .tree-key-chip.is-content{background:var(--tree-content-fill); border-color:var(--content);}
  .tree-key-chip.is-route{border-color:var(--goal);}
  .tree-key-chip.is-next{background:var(--goal-soft); border-color:var(--goal);}

  .tree-key-toggle{
    display:flex; align-items:center; justify-content:space-between; gap:8px;
    width:100%; padding:0; border:none; background:none; cursor:pointer;
    font-family:var(--font-mono); font-size:9.5px; font-weight:700;
    letter-spacing:.12em; text-transform:uppercase; color:var(--text-tertiary);
  }
  .tree-key-toggle:hover{color:var(--text-secondary);}
  .tree-key-toggle svg{width:13px; height:13px; flex-shrink:0;}
  .tree-key-body{display:flex; flex-direction:column; gap:8px;}
  .tree-key.is-collapsed .tree-key-body{display:none;}
  .tree-key.is-collapsed{padding:7px 9px;}

  .tree-key-seg{
    display:flex; align-items:stretch;
    border:1px solid var(--border); border-radius:8px; overflow:hidden;
  }
  .tree-key-seg-label{
    display:flex; align-items:center; padding:0 9px;
    background:var(--bg-surface-3); border-right:1px solid var(--border);
    font-family:var(--font-mono); font-size:9.5px; font-weight:700;
    letter-spacing:.1em; text-transform:uppercase; color:var(--text-tertiary);
  }
  /* Every button carries all four borders and overlaps its neighbour by 1px,
     rather than dropping the right edge on all but the last. Dropping it left
     an active Off/Show with an open right-hand side; overlapping keeps the
     seam a single pixel while letting the active button outline itself
     completely — z-index lifts its border above the neighbours'. */
  .tree-key-seg button{
    flex:1; padding:6px 4px; border:none; cursor:pointer;
    background:var(--bg-surface-2); color:var(--text-secondary);
    font-family:inherit; font-size:11px; font-weight:600;
    transition:background .14s ease, color .14s ease;
  }
  .tree-key-seg button + button{border-left:1px solid var(--border);}
  .tree-key-seg button:hover:not(:disabled){color:var(--text-primary);}
  .tree-key-seg button.active{background:var(--goal-soft); color:var(--goal);}
  .tree-key-seg.is-disabled{opacity:.5;}
  .tree-key-seg.is-disabled button{cursor:not-allowed;}

  /* On a phone the key would eat the canvas, so it drops to a single row of
     chips with the labels hidden — the toggle stays, since it's the only way
     to reach isolate mode. */
  @media (max-width:560px){
    .tree-key{left:10px; bottom:10px; padding:8px; gap:6px;}
    .tree-key-rows{flex-direction:row; gap:6px;}
    .tree-key-row{font-size:0;}
    .tree-key-seg-label{display:none;}
  }

  .tree-controls{
    position:absolute; right:14px; bottom:14px; z-index:5;
    display:flex; flex-direction:column; gap:6px;
  }

  /* ---------- goal overlay on the tree ---------- */

  /* Node states. Weight does the differentiating: the goal + next step are
     solid and heavy, on-route-but-locked is dashed, off-route dims. Completed
     nodes keep their green fill even on the route, and the hover prerequisite
     trace (.hovered-required) wins over all of it — a transient interaction
     shouldn't lose to a persistent overlay. Show mode highlights the route
     over an otherwise-normal tree; Isolate is the mode that removes the rest.
     */
  /* Isolate has no sweep, so it marks route state on the node border itself:
     dashed = locked, solid = actionable. Show mode deliberately leaves the
     border alone — the pulsing ring carries that information there, and
     drawing both would double up. */
  .is-goal-isolate .goal-route .tree-node-rect:not(.complete):not(.hovered-required){
    stroke:var(--goal); stroke-dasharray:5 4; stroke-width:1.6;
  }
  /* Show mode swaps the dashes for the same breathing the edges have. The
     node's delay matches its incoming edge, so the wave passes through
     outline and line together instead of the two animating independently.
     Isolate keeps the dashed treatment — it has no sweep to sync with. */
  /* The pulse is its own ring drawn over the node, exactly as the edge sweep
     is drawn over the base line — never an animation on the node's own
     outline. That keeps the two concerns apart: the node border stays
     whatever it should be (default, complete green, or the amber hover
     trace) and never animates, while the fuchsia ring pulses on top of it.
     Hovering therefore can't make the amber breathe, and because the ring is
     a separate element the animation is never torn off and restarted. */
  .tree-node-pulse{
    fill:none; stroke:var(--goal); stroke-width:1.8; pointer-events:none;
    animation:goalNodeSweep 5s ease-in-out infinite backwards;
  }
  @keyframes goalNodeSweep{
    0%   { stroke-opacity:.20; }
    8%   { stroke-opacity:.9; }
    22%  { stroke-opacity:.20; }
    100% { stroke-opacity:.20; }
  }
  @media (prefers-reduced-motion: reduce){
    .tree-node-pulse{ animation:none; stroke-opacity:.5; }
  }
  .goal-next .tree-node-rect:not(.complete):not(.hovered-required){
    fill:var(--goal-soft); stroke:var(--goal); stroke-dasharray:none; stroke-width:2.4;
  }
  .goal-next .tree-node-label{fill:var(--text-primary); font-weight:700;}
  .is-goal-isolate .goal-ready .tree-node-rect:not(.complete):not(.hovered-required){
    stroke:var(--goal); stroke-dasharray:none; stroke-width:1.8;
  }
  /* Show mode's route: the remaining edges hold a faint fuchsia tint so the
     path is always traceable, then light passes along them in sequence —
     each edge is delayed by its position on the route, so the brightening
     travels from where the player is toward the goal and then rests.

     No moving dash: the effect is opacity only, which reads as the route
     quietly lighting up rather than a marker being animated along a wire. */
  .tree-edge-flow, .tree-edge-flow-halo{ pointer-events:none; }
  .tree-edge-flow{
    stroke-width:2;
    animation:goalSweepCore 5s ease-in-out infinite backwards;
  }
  .tree-edge-flow-halo{
    stroke-width:6;
    animation:goalSweepHalo 5s ease-in-out infinite backwards;
  }
  @keyframes goalSweepCore{
    0%   { opacity:.20; }
    8%   { opacity:.85; }
    22%  { opacity:.20; }
    100% { opacity:.20; }
  }
  @keyframes goalSweepHalo{
    0%   { opacity:0; }
    8%   { opacity:.22; }
    22%  { opacity:0; }
    100% { opacity:0; }
  }
  @media (prefers-reduced-motion: reduce){
    .tree-edge-flow{ animation:none; opacity:.4; }
    .tree-edge-flow-halo{ display:none; }
  }
  /* The destination follows the same pattern as .complete and
     .hovered-required: recolour the stroke and leave the fill alone. Slightly
     heavier than their 1.5 so it carries as an anchor, but no fill change —
     the filled treatment stays reserved for the next step ("you are here"),
     which keeps outline = destination and fill = current distinct. */
  /* Exactly the outline the route nodes pulse to, but frozen at the peak of
     that pulse — same colour, same weight, no animation. The destination is
     the one thing on the route that shouldn't be breathing. */
  .goal-target .tree-node-rect:not(.complete):not(.hovered-required){
    stroke:var(--goal); stroke-dasharray:none; stroke-width:1.8;
    stroke-opacity:1; animation:none;
  }
  .goal-target .tree-node-label{fill:var(--text-primary); font-weight:700;}

  .goal-target .tree-node-tier,
  .goal-next.goal-target .tree-node-tier{fill:var(--goal); font-weight:700;}

  .tree-ctrl-btn{
    width:36px; height:36px; display:flex; align-items:center; justify-content:center;
    background:var(--bg-surface); border:1px solid var(--border); border-radius:9px;
    color:var(--text-secondary); cursor:pointer; padding:0;
    box-shadow:0 2px 8px rgba(0,0,0,0.18); transition:border-color .14s ease, color .14s ease, background .14s ease;
  }
  .tree-ctrl-btn:hover{border-color:var(--accent); color:var(--text-primary); background:var(--bg-surface-2);}
  .tree-ctrl-btn svg{width:18px; height:18px;}
  .tree-ctrl-fit{margin-top:2px;}
  /* The hover lift is applied to the visible elements, NOT the <g> — and a
     stationary transparent hit rect (below, pointer-events:all) is what drives
     :hover. If the lift moved the hover target itself, parking the cursor at a
     card's bottom edge would lift it out from under the cursor, drop :hover,
     settle back, and flicker forever. Decoupling the hit area fixes that. */
  .tree-node-card{cursor:pointer;}
  .tree-node-hit{fill:transparent; pointer-events:all;}
  .tree-node-rect, .tree-node-tier, .tree-node-label{pointer-events:none;}
  .tree-node-tier, .tree-node-label{transition:transform .16s cubic-bezier(.34,1.4,.64,1);}
  .tree-node-card:hover .tree-node-rect,
  .tree-node-card:hover .tree-node-tier,
  .tree-node-card:hover .tree-node-label{transform:translateY(-3px);}
  .tree-node-rect{fill:var(--bg-surface-3); stroke:var(--border); stroke-width:1.5; rx:8; transition:stroke .22s ease, fill .22s ease, filter .18s ease, transform .16s cubic-bezier(.34,1.4,.64,1);}
  .tree-node-rect.complete{fill:var(--tree-complete-fill); stroke:var(--success);}
  /* Admin content marker. Listed after .complete so an authored node reads as
     "has content" (violet) even if it's also been ticked complete — in admin
     mode, authoring status is what you're scanning for. */
  .tree-node-rect.has-content{fill:var(--tree-content-fill); stroke:var(--content);}
  .tree-node-card:hover .tree-node-rect{stroke:rgb(255,196,0); filter:drop-shadow(0 6px 10px rgba(0,0,0,.35));}
  .tree-node-rect:hover{stroke:rgb(255,196,0);}
  .tree-node-rect.hovered-required{fill:var(--tree-hover-fill); stroke:rgb(255,196,0);}
  .tree-edge{transition:stroke .22s ease, stroke-width .22s ease;}
  .tree-edge.hovered-required-edge{stroke:rgb(255,196,0) !important; stroke-width:2.6;}
  .tree-node-label{fill:var(--text-primary); font-size:12px; font-weight:600; font-family:var(--font-body);}
  .tree-node-tier{fill:var(--text-tertiary); font-size:9.5px; font-family:var(--font-mono); letter-spacing:.04em; text-transform:uppercase;}

  /* ---------- search results page ---------- */
  .result-card{display:flex; align-items:flex-start; gap:12px; padding:16px; border:1px solid var(--border-soft); border-radius:var(--radius); background:var(--bg-surface); margin-bottom:8px; cursor:pointer;}
  .result-card:hover{border-color:var(--border);}
  .result-kind{font-family:var(--font-mono); font-size:10px; color:var(--accent); text-transform:uppercase; letter-spacing:.05em; margin-bottom:4px;}
  .result-snippet{font-size:12.5px; color:var(--text-secondary); margin-top:4px;}
  .result-snippet mark{background:var(--accent-soft); color:var(--text-primary); border-radius:2px;}

  /* ---------- toast ---------- */
  #toast-root{position:fixed; bottom:22px; right:22px; display:flex; flex-direction:column; gap:8px; z-index:100;}
  .toast{
    background:var(--bg-surface-3); border:1px solid var(--border); color:var(--text-primary);
    padding:11px 16px; border-radius:var(--radius-sm); font-size:13px; font-weight:500;
    box-shadow:0 8px 24px rgba(0,0,0,.4);
    display:flex; align-items:center; gap:8px;
  }
  .toast.success{border-color:var(--success); }
  .toast-dot{width:7px;height:7px;border-radius:50%; background:var(--accent); flex-shrink:0;}
  .toast.success .toast-dot{background:var(--success);}

  /* ---------- responsive ---------- */
  @media (max-width:860px){
    .dash-grid{grid-template-columns:1fr;}
    .nav-cards{grid-template-columns:1fr 1fr;}
  }
  @media (max-width:680px){
    .sidebar{display:none;}
    .content{padding:18px;}
    .topbar{padding:calc(12px + env(safe-area-inset-top, 0px)) 16px 12px 16px;}
    .rank-hero{margin:-18px -18px 18px; padding:28px 18px 24px;}
    .rank-hero::before{
      /* On mobile the sidebar is hidden, so simply span the whole viewport
         width — the --sidebar-w-based formula above would inset incorrectly. */
      left:calc(50% - 50vw);
      right:calc(50% - 50vw);
    }
    .nav-cards{grid-template-columns:1fr;}
    .bottom-nav{
      display:flex; position:fixed; bottom:0; left:0; right:0; height:58px;
      background:var(--bg-surface); border-top:1px solid var(--border-soft); z-index:50;
    }
    .bottom-nav-item{flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px; background:none; border:none; color:var(--text-tertiary); font-size:10px; font-weight:600;}
    .bottom-nav-item svg{width:18px;height:18px;}
    .bottom-nav-item.active{color:var(--accent);}
    .app-shell{padding-bottom:58px;}
    /* Sidebar is hidden on mobile, so the fixed glossary bar spans the full
       width and sits just above the 58px bottom nav. */
    .glossary-bottom{ left:0; bottom:58px; padding:28px 0 12px; }
    .prereq-row{gap:18px;}
    /* Reveal the compact account button in the header on mobile. */
    .topbar-account{display:block;}
    /* The search bar no longer needs to stretch the full row now that the
       account button shares the header; cap it so both sit comfortably. */
    .search-wrap{max-width:none;}
  }

  /* ---------- mobile "More" overflow sheet ---------- */
  .more-sheet-overlay{
    position:fixed; inset:0; z-index:60;
    background:rgba(0,0,0,0.5);
    display:flex; align-items:flex-end;
    animation:moreFade .18s ease;
  }
  @keyframes moreFade{ from{opacity:0;} to{opacity:1;} }
  .more-sheet{
    width:100%;
    background:var(--bg-surface);
    border-top-left-radius:16px; border-top-right-radius:16px;
    border-top:1px solid var(--border-soft);
    padding:10px 14px calc(14px + env(safe-area-inset-bottom, 0px));
    box-shadow:0 -10px 30px rgba(0,0,0,0.4);
    animation:moreSlideUp .22s cubic-bezier(.32,.72,0,1);
  }
  @keyframes moreSlideUp{ from{transform:translateY(100%);} to{transform:translateY(0);} }
  .more-sheet-handle{
    width:38px; height:4px; border-radius:999px; background:var(--border);
    margin:2px auto 12px;
  }
  .more-sheet-list{display:flex; flex-direction:column; gap:4px;}
  .more-sheet-item{
    display:flex; align-items:center; gap:14px;
    width:100%; text-align:left; cursor:pointer;
    padding:14px 12px; border-radius:var(--radius);
    background:none; border:none; color:var(--text-primary);
  }
  .more-sheet-item:hover{background:var(--bg-surface-2);}
  .more-sheet-item.active{background:var(--accent-soft);}
  .more-sheet-icon{
    width:34px; height:34px; border-radius:9px; flex-shrink:0;
    display:flex; align-items:center; justify-content:center;
    background:var(--bg-surface-3); color:var(--text-secondary);
  }
  .more-sheet-item.active .more-sheet-icon{background:var(--accent-soft); color:var(--accent);}
  .more-sheet-icon svg{width:18px; height:18px;}
  .more-sheet-label{flex:1; font-size:15px; font-weight:600;}
  .more-sheet-item.active .more-sheet-label{color:var(--accent);}
  .more-sheet-chevron{color:var(--text-tertiary); display:flex;}
  .more-sheet-chevron svg{width:16px; height:16px;}
  .more-sheet-check{color:var(--accent); display:flex;}
  .more-sheet-check svg{width:17px; height:17px;}

  /* ============================================================
     ADMIN MODE
     ============================================================ */
  .admin-toggle.on{ opacity:1; color:var(--accent); background:var(--accent-soft); }
  .save-website-btn{ width:100%; justify-content:center; margin-bottom:8px; }
  .admin-badge{
    font-family:var(--font-mono); font-size:9.5px; font-weight:700; letter-spacing:.06em;
    padding:2px 7px; border-radius:4px; background:var(--accent-soft); color:var(--accent);
    margin-left:8px; vertical-align:middle;
  }
  .list-header-row{display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:26px;}
  .list-header-row .page-title, .list-header-row .page-sub{margin-bottom:0;}
  .icon-btn{
    width:28px; height:28px; border-radius:6px; border:1px solid var(--border); background:var(--bg-surface-2);
    color:var(--text-secondary); display:flex; align-items:center; justify-content:center; cursor:pointer; flex-shrink:0;
  }
  .icon-btn svg{width:13px;height:13px;}
  .icon-btn:hover{border-color:var(--accent-soft-2); color:var(--text-primary);}
  .icon-btn.danger:hover{border-color:var(--danger); color:var(--danger); background:var(--danger-soft);}
  /* Reorder arrows at the ends of a group stay visible but inert, so the row's
     control layout doesn't shift as items move up and down the list. */
  .icon-btn:disabled{opacity:.32; cursor:default;}
  .icon-btn:disabled:hover{border-color:var(--border); color:var(--text-secondary);}
  .row-admin-actions{display:flex; gap:6px; margin-left:auto; margin-right:4px;}
  .detail-admin-actions{display:flex; gap:8px;}

  /* ---------- wizard shell ---------- */
  .wizard{max-width:680px;}
  .wizard-header{margin-bottom:26px;}
  .wizard-eyebrow{font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--accent); margin-bottom:14px;}
  .wizard-steps-track{display:flex; align-items:center; margin-bottom:12px;}
  .wizard-step-dot{
    width:24px;height:24px; border-radius:50%; background:var(--bg-surface-3); color:var(--text-tertiary);
    display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700; flex-shrink:0;
    font-family:var(--font-mono); border:1.5px solid var(--border);
  }
  .wizard-step-dot.active{background:var(--accent); color:var(--on-accent); border-color:var(--accent);}
  .wizard-step-dot.done{background:var(--success-soft); color:var(--success); border-color:var(--success);}
  .wizard-step-line{flex:1; height:1.5px; background:var(--border); margin:0 2px; min-width:8px;}
  .wizard-step-name{font-size:13px; color:var(--text-secondary); font-weight:600;}
  .wizard-step-title{font-size:19px; font-weight:700; margin:0 0 14px;}
  .wizard-body{min-height:240px; margin-bottom:24px;}
  .wizard-footer{display:flex; align-items:center; gap:10px; padding-top:18px; border-top:1px solid var(--border-soft);}

  .wizard-input, .wizard-textarea{
    width:100%; background:var(--bg-surface-2); border:1px solid var(--border); color:var(--text-primary);
    border-radius:var(--radius-sm); padding:9px 12px; font-size:13.5px; font-family:inherit; resize:vertical;
  }
  .wizard-input.large{font-size:17px; padding:12px 14px;}
  .wizard-input.small{font-size:13px; padding:7px 10px; margin-bottom:10px;}
  .wizard-input:focus, .wizard-textarea:focus{border-color:var(--accent-soft-2);}
  /* Auto-growing paragraph/quote/tip/warning editors: expand to fit content
     (JS sets height to scrollHeight on input) instead of showing a scrollbar. */
  .wizard-textarea.autogrow{resize:none; overflow:hidden; min-height:44px;}

  .tier-select-row{display:flex; gap:8px; flex-wrap:wrap;}
  .tier-chip{
    font-family:var(--font-mono); font-size:12px; font-weight:700; letter-spacing:.04em; text-transform:uppercase;
    padding:8px 14px; border-radius:7px; border:1px solid var(--border); background:var(--bg-surface-2);
    color:var(--text-secondary); cursor:pointer;
  }
  .tier-chip:hover{border-color:var(--accent-soft-2);}
  .tier-chip.selected{background:var(--accent-soft); border-color:var(--accent); color:var(--accent);}
  /* physics/theory picker in the article wizard */
  .kind-select{display:flex; gap:10px; flex-wrap:wrap;}
  .kind-option{display:flex; align-items:center; gap:8px; padding:10px 16px; border-radius:9px; border:1px solid var(--border); background:var(--bg-surface-2); color:var(--text-secondary); cursor:pointer; font-size:13px; font-weight:600;}
  .kind-option svg{width:18px; height:18px; flex-shrink:0;}
  .kind-option:hover{border-color:var(--accent-soft-2);}
  .kind-option.selected{background:var(--accent-soft); border-color:var(--accent); color:var(--accent);}

  .chip.removable{padding-right:6px;}
  .chip.removable button{
    background:none; border:none; color:inherit; cursor:pointer; font-size:14px; line-height:1; padding:0 2px;
    margin-left:2px; opacity:.7;
  }
  .chip.removable button:hover{opacity:1;}

  .picker-wrap{position:relative; display:inline-block;}
  .picker-popover{
    position:absolute; top:calc(100% + 6px); left:0; min-width:220px; max-height:260px; overflow-y:auto;
    background:var(--bg-surface-2); border:1px solid var(--border); border-radius:var(--radius);
    box-shadow:0 12px 28px rgba(0,0,0,.45); z-index:25;
  }
  .picker-option{padding:9px 14px; font-size:13px; cursor:pointer;}
  .picker-option:hover{background:var(--bg-surface-3);}
  .picker-empty{padding:12px 14px; font-size:12.5px; color:var(--text-tertiary);}

  .wizard-confirm{background:var(--bg-surface); border:1px solid var(--border-soft); border-radius:var(--radius-lg); padding:6px 18px;}
  .confirm-row{display:flex; gap:18px; padding:13px 0; border-bottom:1px solid var(--border-soft);}
  .confirm-row:last-child{border-bottom:none;}
  .confirm-label{width:110px; flex-shrink:0; font-size:12px; font-weight:700; color:var(--text-tertiary); text-transform:uppercase; letter-spacing:.05em; padding-top:2px;}
  .confirm-value{font-size:14px; color:var(--text-primary); line-height:1.6;}

  /* ---------- content editor ---------- */
  .editor-wrap{display:flex; flex-direction:column; gap:8px;}
  .editor-tile{border:1px solid var(--border-soft); border-radius:var(--radius); background:var(--bg-surface);}
  .editor-tile.dragging{opacity:.4;}
  .editor-tile.drag-over{border-color:var(--accent); box-shadow:0 0 0 1px var(--accent);}
  .editor-tile-head{display:flex; align-items:center; gap:10px; padding:9px 10px;}
  .drag-handle{cursor:grab; color:var(--text-tertiary); font-size:12px; letter-spacing:-2px; flex-shrink:0; user-select:none;}
  .editor-tile-type-tag{
    font-family:var(--font-mono); font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.04em;
    color:var(--accent); background:var(--accent-soft); padding:3px 7px; border-radius:4px; flex-shrink:0;
  }
  .editor-tile-summary{font-size:12.5px; color:var(--text-tertiary); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1;}
  .editor-tile-actions{display:flex; gap:4px; margin-left:auto; flex-shrink:0;}
  .editor-tile-actions button{
    width:24px;height:24px; border-radius:5px; border:1px solid transparent; background:none; color:var(--text-tertiary);
    cursor:pointer; display:flex; align-items:center; justify-content:center; font-size:11px;
  }
  .editor-tile-actions button:hover{background:var(--bg-surface-3); color:var(--text-primary);}
  .editor-tile-actions button.danger:hover{color:var(--danger); background:var(--danger-soft);}
  .editor-tile-actions svg{width:13px;height:13px;}
  .editor-tile-body{padding:0 12px 12px 12px;}
  .editor-nested{padding:0 0 4px 0; display:flex; flex-direction:column; gap:8px;}
  .editor-nested > .editor-wrap{padding-left:14px; border-left:2px solid var(--border-soft); margin-left:2px;}

  .editor-add-toolbar{display:flex; flex-wrap:wrap; gap:6px; padding:6px 2px;}
  .editor-add-btn{
    font-size:12px; font-weight:600; padding:6px 11px; border-radius:6px; border:1px dashed var(--border);
    background:none; color:var(--text-secondary); cursor:pointer;
  }
  .editor-add-btn:hover{border-color:var(--accent-soft-2); color:var(--accent); background:var(--accent-soft);}
  .editor-add-btn.small{padding:5px 9px; font-size:11.5px;}

  .editor-tabs-row{display:flex; gap:6px; flex-wrap:wrap; align-items:center; margin:8px 0 12px;}
  .editor-tab-chip{display:flex; align-items:center; gap:2px; background:var(--bg-surface-3); border:1px solid var(--border); border-radius:6px; padding:2px 2px 2px 8px;}
  .tab-label-input{background:none; border:none; color:var(--text-primary); font-size:12.5px; font-weight:600; width:90px; padding:4px 2px;}
  .tab-remove{background:none; border:none; color:var(--text-tertiary); cursor:pointer; font-size:14px; width:20px; height:20px;}
  .tab-remove:hover{color:var(--danger);}
  .editor-tab-panel{border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:10px; margin-bottom:8px; background:var(--bg-surface-2);}
  .editor-tab-panel-label{font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.05em; color:var(--text-tertiary); margin-bottom:8px;}

  .media-fields{display:flex; flex-direction:column; gap:8px;}
  .media-source-row{display:flex; gap:8px; align-items:center;}
  .media-source-row .wizard-input{flex:1;}
  .media-upload-btn{display:inline-flex; align-items:center; gap:6px; white-space:nowrap; cursor:pointer; font-size:12.5px; padding:9px 12px;}
  .media-preview{background:var(--bg-base); border:1px solid var(--border-soft); border-radius:var(--radius-sm); padding:10px; display:flex; justify-content:center;}
  .media-preview img{max-width:100%; max-height:160px; border-radius:4px;}
  .media-preview video{max-width:100%; max-height:200px; border-radius:4px;}

  .empty-note.small{padding:8px 2px; font-size:12px;}

  /* ============================================================
     SETTINGS PAGE
     ============================================================ */
  .settings-search-bar{
    position:sticky; top:0; z-index:5; background:var(--bg-base);
    padding:2px 0 16px; margin-bottom:4px;
  }
  .settings-search-bar .search-input{padding-left:34px;}
  .settings-search-wrap{position:relative; max-width:420px;}
  .settings-search-wrap .search-icon{position:absolute; left:11px; top:50%; transform:translateY(-50%); width:15px;height:15px; color:var(--text-tertiary); pointer-events:none;}
  .device-filter{display:flex; gap:6px; margin-top:10px; flex-wrap:wrap;}
  .device-filter-btn{
    display:inline-flex; align-items:center; gap:7px;
    padding:6px 13px; border-radius:999px;
    background:var(--surface-2,rgba(255,255,255,0.04));
    border:1px solid var(--border,rgba(255,255,255,0.09));
    color:var(--text-secondary); font-size:13px; font-weight:600;
    cursor:pointer; transition:background .12s, border-color .12s, color .12s;
  }
  .device-filter-btn:hover{ color:var(--text-primary); border-color:var(--border-strong,rgba(255,255,255,0.18)); }
  .device-filter-btn.active{
    background:var(--accent,#ffc400); border-color:var(--accent,#ffc400);
    color:#141414;
  }
  .device-filter-count{
    font-size:11px; font-weight:700; min-width:17px; height:17px; padding:0 5px;
    display:inline-flex; align-items:center; justify-content:center;
    border-radius:999px; background:rgba(0,0,0,0.16); color:inherit;
  }
  .device-filter-btn:not(.active) .device-filter-count{ background:rgba(255,255,255,0.08); color:var(--text-tertiary); }
  body.light-mode .device-filter-btn:not(.active) .device-filter-count{ background:rgba(0,0,0,0.06); }

  .player-card{border:1px solid var(--border-soft); border-radius:var(--radius-lg); background:var(--bg-surface); margin-bottom:10px; overflow:hidden;}
  .player-card-head{display:flex; align-items:center; gap:10px; padding:14px 16px; cursor:pointer;}
  .player-card-head:hover{background:var(--bg-surface-2);}
  .player-card-name{font-weight:700; font-size:14.5px; flex:1;}
  .player-card-name-input{
    flex:1; background:var(--bg-surface-2); border:1px solid var(--border); color:var(--text-primary);
    border-radius:var(--radius-sm); padding:7px 10px; font-size:14px; font-weight:600;
  }
  .player-card-chevron{width:15px;height:15px; color:var(--text-tertiary); transition:transform .15s ease; flex-shrink:0;}
  .player-card.open .player-card-chevron{transform:rotate(180deg);}
  .player-card-body{padding:4px 16px 18px;}
  .player-card-body.hidden{display:none;}

  .field-grid{display:grid; grid-template-columns:repeat(auto-fit, minmax(150px,1fr)); gap:14px; margin-bottom:14px;}
  /* Camera settings are a fixed set of 8 fields; pin them to 4 columns so they
     lay out as a tidy 2×4 block rather than reflowing to an uneven 5+3 the way
     auto-fit does at wide widths. Collapses to 2 columns on narrow screens. */
  .field-grid.camera-grid{grid-template-columns:repeat(4, 1fr);}
  @media (max-width:640px){ .field-grid.camera-grid{grid-template-columns:repeat(2, 1fr);} }
  .field-block{display:flex; flex-direction:column; gap:6px;}
  .field-label{font-size:11px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; color:var(--text-tertiary);}
  .field-value-readonly{font-size:14px; color:var(--text-primary); font-weight:600;}

  /* generic on/off toggle - visually matches complete-toggle */
  .bool-toggle{display:flex; align-items:center; gap:9px; cursor:pointer; background:none; border:none; padding:0; color:var(--text-secondary); font-size:13px; font-weight:600;}
  .bool-toggle .toggle-track{width:30px; height:17px; border-radius:999px; background:#454c58; position:relative; flex-shrink:0;}
  .bool-toggle .toggle-knob{width:13px;height:13px; border-radius:50%; background:#cdd3db; position:absolute; top:2px; left:2px; transition:left .12s ease;}
  .bool-toggle.on .toggle-knob{left:15px;}
  .bool-toggle.readonly{cursor:default;}

  .view-binds-btn{margin-top:4px;}

  /* ---------- modal / overlay ---------- */
  .modal-overlay{
    position:fixed; inset:0; background:rgba(8,9,12,.7); backdrop-filter:blur(2px);
    display:flex; align-items:center; justify-content:center; z-index:200; padding:24px;
    animation:modalOverlayIn .16s ease both;
  }
  .modal-panel{
    background:var(--bg-surface); border:1px solid var(--border); border-radius:var(--radius-lg);
    max-width:720px; width:100%; max-height:88vh; display:flex; flex-direction:column;
    box-shadow:0 24px 60px rgba(0,0,0,.55);
    animation:modalPanelIn .22s cubic-bezier(.33,0,.2,1) both;
  }
  .modal-panel.modal-panel-compact{max-width:400px;}
  /* Modal entrances: the overlay fades in while the panel scales up from 96%
     and rises a few px, settling with an ease-out (no bounce, to stay calm).
     Entrance-only — modals are removed from the DOM instantly on close. */
  @keyframes modalOverlayIn{ from{opacity:0;} to{opacity:1;} }
  @keyframes modalPanelIn{
    from{opacity:0; transform:scale(.96) translateY(8px);}
    to{opacity:1; transform:scale(1) translateY(0);}
  }
  @media (prefers-reduced-motion: reduce){
    .modal-overlay, .modal-panel{ animation:none; }
  }
  .tracker-select{
    width:100%; appearance:none; -webkit-appearance:none;
    background:var(--bg-surface-2); border:1px solid var(--border); color:var(--text-primary);
    border-radius:var(--radius-sm); padding:9px 34px 9px 12px; font-size:13.5px; font-weight:500;
    cursor:pointer; font-family:inherit;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238b93a1' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat:no-repeat; background-position:right 11px center;
  }
  .tracker-select:hover{border-color:var(--accent-soft-2);}
  .tracker-select:focus{outline:none; border-color:var(--accent);}
  .modal-head{display:flex; align-items:center; justify-content:space-between; gap:12px; padding:18px 22px; border-bottom:1px solid var(--border-soft); flex-shrink:0;}
  .modal-title{font-size:16px; font-weight:700;}
  .modal-close{width:30px;height:30px; border-radius:7px; border:1px solid var(--border); background:var(--bg-surface-2); color:var(--text-secondary); display:flex; align-items:center; justify-content:center; cursor:pointer; flex-shrink:0;}
  .modal-close:hover{color:var(--text-primary); border-color:var(--accent-soft-2);}
  .modal-close svg{width:14px;height:14px;}
  .modal-body{padding:20px 22px; overflow-y:auto; flex:1;}
  .modal-footer{display:flex; align-items:center; gap:10px; padding:16px 22px; border-top:1px solid var(--border-soft); flex-shrink:0;}

  /* ---------- bind rows ---------- */
  .device-select-row{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:6px;}
  .bind-row{display:flex; align-items:center; gap:12px; padding:11px 0; border-bottom:1px solid var(--border-soft);}
  .bind-row:last-child{border-bottom:none;}
  .bind-row-label{width:170px; flex-shrink:0; font-size:13px; font-weight:600; color:var(--text-secondary);}
  .bind-row-controls{flex:1; display:flex; align-items:center; gap:8px; flex-wrap:wrap;}
  .bind-readonly-value{font-family:var(--font-mono); font-size:12.5px; font-weight:700; padding:4px 10px; border-radius:6px; background:var(--bg-surface-3); border:1px solid var(--border); color:var(--text-primary);}
  .bind-chip-grid{display:flex; gap:6px; flex-wrap:wrap;}
  .bind-chip{
    font-family:var(--font-mono); font-size:11.5px; font-weight:700; letter-spacing:.03em;
    padding:6px 11px; border-radius:7px; border:1.5px solid var(--border); background:var(--bg-surface-2);
    color:var(--text-secondary); cursor:pointer;
  }
  .bind-chip.selected{background:var(--accent-soft); border-color:var(--accent); color:var(--accent);}
  .bind-cat-chip{font-size:10px; padding:4px 7px; border-radius:5px;}
  .bind-select{
    background:var(--bg-surface-2); border:1px solid var(--border); color:var(--text-primary);
    border-radius:var(--radius-sm); padding:6px 9px; font-size:12.5px; font-family:inherit; min-width:140px;
  }
  .face-icon-row{display:inline-flex; align-items:center; gap:8px;}
  .face-btn-cap{
    display:inline-flex; align-items:center; justify-content:center; width:26px; height:26px;
    border-radius:50%; background:#3a4049; flex-shrink:0;
  }
  .face-btn-cap.xbox-cap{font-family:var(--font-body); font-weight:800; font-size:15.6px; color:#15171c;}

  .controller-values-grid{display:grid; grid-template-columns:repeat(auto-fit, minmax(190px,1fr)); gap:14px; margin-top:8px;}
  .field-label-row{display:flex; align-items:center; justify-content:space-between; gap:8px; margin-bottom:4px;}
  .field-label-row .field-label{margin-bottom:0;}
  .value-hide-btn{
    display:inline-flex; align-items:center; justify-content:center;
    width:24px; height:24px; padding:0; flex-shrink:0;
    background:none; border:none; border-radius:6px; cursor:pointer;
    color:var(--text-tertiary); transition:color .14s ease, background .14s ease;
  }
  .value-hide-btn svg{width:15px; height:15px;}
  .value-hide-btn:hover{color:var(--text-secondary); background:var(--bg-surface-3);}
  .value-hide-btn.is-hidden{color:var(--accent);}
  .field-value-hidden{color:var(--text-tertiary); font-style:italic;}
  .value-block-hidden{opacity:0.6;}

  /* ============================================================
     TRAINING PACKS
     ============================================================ */
  .pack-filter-row{display:flex; align-items:center; gap:10px; margin-bottom:16px;}
  .training-pack-row{
    display:flex; align-items:center; justify-content:space-between; gap:18px; flex-wrap:wrap;
    padding:9px 16px; border-radius:var(--radius); border:1px solid var(--border-soft);
    background:var(--bg-surface); margin-bottom:6px;
  }
  .training-pack-left{flex:1; min-width:160px; display:flex; align-items:center;}
  .training-pack-right{display:flex; align-items:stretch; gap:8px; flex-shrink:0;}
  .training-pack-right .icon-btn{height:auto; width:36px;}
  .training-code-box{
    font-family:var(--font-mono); font-size:13px; font-weight:700; letter-spacing:.04em;
    padding:8px 14px; border-radius:var(--radius-sm); background:var(--bg-surface-3); border:1px solid var(--border);
    color:var(--text-primary); white-space:nowrap;
  }
  .training-code-input{font-family:var(--font-mono); font-weight:700; letter-spacing:.04em; width:190px;}

  .pack-name-lg{font-size:17px; font-weight:700; color:var(--text-primary);}
  .pack-name-input{
    font-size:16px; font-weight:700; background:var(--bg-surface-2); border:1px solid var(--border);
    color:var(--text-primary); border-radius:var(--radius-sm); padding:7px 11px; width:220px;
  }
  .pack-title-wrap{position:relative; display:inline-flex; align-items:center; gap:8px; cursor:default; padding:4px 0;}
  .pack-mechanics-dropdown{
    display:none; position:absolute; top:calc(100% + 4px); left:0; min-width:260px; max-width:380px;
    background:var(--bg-surface-2); border:1px solid var(--border); border-radius:var(--radius);
    box-shadow:0 12px 28px rgba(0,0,0,.5); z-index:30; padding:12px 14px;
  }
  .pack-title-wrap:hover .pack-mechanics-dropdown{display:block;}
  /* Even without a stray stacking context, sibling rows later in the DOM would
     paint over an earlier row's dropdown. Giving the row a position and
     raising it while it's hovered (or while its admin editor is open) puts the
     whole row — dropdown included — above the ones beneath it. */
  .training-pack-row{position:relative;}
  .training-pack-row:hover,
  .training-pack-row:focus-within,
  .training-pack-row.is-editing{z-index:40;}
  .pack-mechanics-dropdown.always-open{display:block;}
  .pack-dropdown-section{margin-bottom:10px;}
  .pack-dropdown-section:last-child{margin-bottom:0;}
  .pack-dropdown-label{font-size:10px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--text-tertiary); margin-bottom:6px;}

  .icon-btn.copy-btn{background:var(--accent); border-color:var(--accent); color:var(--on-accent);}
  .icon-btn.copy-btn:hover{filter:brightness(1.08);}
  .icon-btn.copy-btn.copied{background:var(--success); border-color:var(--success);}

  @media (max-width:560px){
    .bind-row{flex-direction:column; align-items:flex-start; gap:6px;}
    .bind-row-label{width:auto;}
  }
