  /* Inter and Cinzel (SIL Open Font License, fonts/OFL-*.txt), served from here rather
     than Google Fonts, so opening the page asks no one else for anything. One variable
     file per alphabet; the browser fetches only the ones the page's text needs. */
  @font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/inter-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
  @font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/inter-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF}
  @font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/inter-cyrillic.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
  @font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/inter-cyrillic-ext.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F}
  @font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/inter-greek.woff2) format('woff2');unicode-range:U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF}
  @font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/inter-greek-ext.woff2) format('woff2');unicode-range:U+1F00-1FFF}
  @font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(fonts/inter-vietnamese.woff2) format('woff2');unicode-range:U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB}
  @font-face{font-family:'Cinzel';font-style:normal;font-weight:600 700;font-display:swap;src:url(fonts/cinzel-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD}
  @font-face{font-family:'Cinzel';font-style:normal;font-weight:600 700;font-display:swap;src:url(fonts/cinzel-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF}
  :root{
    /* Hextech. The League client is blue-black metal, not neutral grey, and its
       text is warm parchment (#f0e6d2 / #a09b8c) where ours was blue-white — that
       temperature, more than any one colour, is what made this read as a generic
       dark app. Faint is lifted to 4.8:1 on a card: the old #5d6577 was 3.2:1,
       below what small text needs to be read.
       2.3.3, after a week on an iPad: the hairlines were 1.25:1 against a card —
       a button's outline and a field's edge were there in the file and nowhere on
       the screen. Dividers now sit at about 1.55:1, the edges of things you can
       press or type into (--edge) at 2:1, hover at 2.5:1, and faint text at
       5.5:1 instead of 4.8. Still hairlines, still quiet; now visible. */
    /* Graphite, not navy. Every layer used to share the client's blue-black, and
       every frame, chip and wash was tinted on top of it, so the page read as one
       colour: the accent had nothing neutral to stand out from, and a blue accent
       made the whole thing blue. The base is a near-neutral cool grey now, and
       colour is kept for what means something: the accents, the tiers, a state. */
    --bg:#090c10; --panel:#12161c; --panel2:#171c23; --panel3:#1e242c;
    --line:#323a44; --edge:#404a56; --line2:#53606d;
    /* accent — live-overridden by the accent picker in Settings. The defaults are
       the client's own gold and blue; the derived ones are what applyAccent()
       computes from them, so a reset lands exactly here. */
    --gold:#c8aa6e; --gold-dim:#7b663b; --gold-mid:#b69b67; --star:#e9d7b3;
    /* The accent as *text*. A raw accent is picked to look good as a fill, and at
       full chroma on a near-black panel it vibrates and reads as glare rather than
       as a label — a deep one goes the other way and disappears. These two are the
       same hue held inside a band that stays legible either way, and every place
       the accent is used for type reads from them instead of the raw colour. */
    --gold-txt:#d2ba89; --teal-txt:#73e8df;
    --text:#f0e6d2; --muted:#a09b8c; --faint:#938e83;
    /* secondary accent — also live-overridden from Settings. --teal-ink is the
       text colour for anything filled with --teal; applyAccent2 flips it between
       near-black and white by luminance, because a fixed dark ink goes unreadable
       the moment somebody picks a deep secondary. */
    --teal:#0ac8b9; --teal-ink:#002e2a; --danger:#ff6b6b; --good:#4ade80;
    /* What a state means is not the user's to recolour. Green is on, done, fine;
       amber is "worth a look"; red is lost, banned, or cannot be undone. The two
       accents are for the user's own colours and for actions — "2-step on" used to
       borrow --teal, which in a gold theme made it the same gold as "No 2-step"
       beside it, the good state and the warning one indistinguishable. */
    --warn:#f0c674; --warn-txt:#f7d690;
    --ts:1; /* Settings → Text size: every font size in the app is multiplied by it */
    /* elevation */
    --sh-1:0 1px 2px #00000059;
    /* Satin, the one finish every raised control wears: lit softly from above,
       shaded at its foot, sitting a pixel proud of the page. A broad, low light
       with no hard band in it, so it reads as satin rather than chrome, and it is
       the same on every button in every accent. --sheen-fill is the same light on
       a filled plate (Login), which takes a little more to show on a bright fill. */
    --sheen:linear-gradient(180deg,#ffffff26 0%,#ffffff0d 45%,#ffffff00 62%,#00000024 100%);
    --sheen-fill:linear-gradient(180deg,#ffffff59 0%,#ffffff17 46%,#ffffff00 62%,#0000001f 100%);
    --sh-btn:inset 0 1px 0 #ffffff1a,inset 0 -1px 0 #00000040,0 1px 2px #00000066;
    --sh-fill:inset 0 1px 0 #ffffff73,inset 0 -1px 0 #00000038,0 1px 3px #00000073;
    --sh-2:0 2px 4px #0000003d,0 8px 24px -10px #000000a8;
    --sh-3:0 4px 10px #0000004a,0 24px 56px -18px #000000cc;
    /* Cut metal, not moulded plastic: the client's frames are near-square, and
       eighteen-pixel corners plus twenty-seven pills were most of what made this
       read as any other web app. --r-xs is the plate a label or a count sits on. */
    --r-xs:3px; --r-sm:4px; --r-md:6px; --r-lg:8px;
  }
  *{box-sizing:border-box}
  /* The <meta name="color-scheme"> up top does not reach the computed style, so
     the browser was still treating this as a light page: the native <select>
     dropdown opened white on white, and the scrollbar drew a light track behind
     the tinted thumb. This is the declaration that actually decides it. */
  :root{color-scheme:dark}
  html{scroll-behavior:smooth}
  /* the scrollbar is the one chrome element that touches every screen, so it
     carries the primary accent. It was mixed only 42% into the neutral, which on
     a muted accent is a tint you have to be told about — it now reads as the
     colour you picked. Plain hex first for anything without color-mix. */
  *{scrollbar-width:thin;scrollbar-color:#3a4152 transparent;
    scrollbar-color:color-mix(in srgb,var(--gold) 72%,#2a3040) transparent}
  ::-webkit-scrollbar{width:12px;height:12px}
  ::-webkit-scrollbar-track{background:transparent}
  ::-webkit-scrollbar-thumb{background:#3a4152;background:color-mix(in srgb,var(--gold) 72%,#2a3040);
    border-radius:99px;border:3px solid transparent;background-clip:content-box}
  ::-webkit-scrollbar-thumb:hover{background:#4a5266;background:var(--gold);background-clip:content-box}
  ::-webkit-scrollbar-corner{background:transparent}
  /* the popup list is drawn by the OS, which only takes plain colours — no
     color-mix, no custom properties it can't resolve at paint time */
  option{background-color:#171c23;color:#e9edf5}
  /* The account password and the API key are masked with CSS rather than being
     type="password" inputs. A real password field next to a text field the
     browser reads as a username is all Chrome's heuristics need: filling both
     programmatically (which is what opening an account for editing does) and
     then hiding the panel looks exactly like a submitted sign-in form, so it
     offered to save credentials for a site the user was never signing in to —
     at random moments, for whichever account had last been opened.
     These are stored secrets in a local vault, not a login this page performs. */
  .mask{-webkit-text-security:disc;text-security:disc;
    font-family:ui-monospace,'SF Mono',Consolas,monospace;letter-spacing:.06em}
  ::selection{background:color-mix(in srgb,var(--gold) 32%,transparent);color:#fff}

  body{margin:0;position:relative;color:var(--text);
    font-family:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
    font-size:calc(14px*var(--ts));line-height:1.55;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
    background:var(--bg);background-attachment:fixed}
  /* Atmosphere: Settings picks one of six looks.
     Spotlight + Aurora = fixed viewport light (stays while you scroll).
     Lattice + Stardust = page texture that scrolls with the document and fades
     out near the end. Noir = fixed vignette + grain, no accent colour. */
  /* One light, from above. This was two washes: the primary accent from the top
     left and the secondary from the top right, and where they met they mixed. A
     blue and a gold made an olive-teal haze over the whole header. One neutral
     light gives the page a top, and a little of the primary tints it, in one hue
     only; the secondary is left to the controls. */
  body[data-atmosphere="spotlight"]{
    background:
      radial-gradient(64vw 52vh at 50% -16%,color-mix(in srgb,var(--gold) 9%,transparent) 0%,transparent 70%),
      radial-gradient(110vw 72vh at 50% -28%,#ffffff0b 0%,transparent 62%),
      var(--bg);
    background-attachment:fixed}
  body[data-atmosphere="spotlight"]::before{content:none}

  /* Same fixed behaviour as Spotlight, but a balanced pair of washes — left and
     right mirrored — so it does not read as one teal blob and one gold blob. */
  body[data-atmosphere="aurora"]{
    background:
      radial-gradient(80vw 64vh at 0% 0%,color-mix(in srgb,var(--gold) 20%,transparent) 0%,transparent 66%),
      radial-gradient(80vw 64vh at 100% 0%,color-mix(in srgb,var(--teal) 18%,transparent) 0%,transparent 66%),
      var(--bg);
    background-attachment:fixed}
  body[data-atmosphere="aurora"]::before{content:none}

  /* Darkened edges + heavier grain. Easy to miss next to colourful modes — the
     point is "night", not another wash. */
  body[data-atmosphere="noir"]{
    background:
      radial-gradient(ellipse 70% 55% at 50% 40%,#11151b 0%,var(--bg) 72%),
      var(--bg);
    background-attachment:fixed}
  body[data-atmosphere="noir"]::before{content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
    background:radial-gradient(ellipse 62% 52% at 50% 38%,transparent 20%,#000000c8 100%)}

  /* Grid / dots tile the full document; the mask dissolves them slowly. */
  body[data-atmosphere="lattice"]{background:var(--bg)}
  body[data-atmosphere="lattice"]::before{content:"";position:absolute;left:0;right:0;top:0;bottom:0;
    z-index:0;pointer-events:none;
    background-image:
      linear-gradient(#ffffff08 1px,transparent 1px),
      linear-gradient(90deg,#ffffff08 1px,transparent 1px);
    background-size:48px 48px;
    -webkit-mask-image:linear-gradient(to bottom,#000 0%,#000 22%,#000000c8 48%,#00000070 72%,#00000030 88%,#0000 97%);
    mask-image:linear-gradient(to bottom,#000 0%,#000 22%,#000000c8 48%,#00000070 72%,#00000030 88%,#0000 97%)}

  body[data-atmosphere="stardust"]{background:var(--bg)}
  body[data-atmosphere="stardust"]::before{content:"";position:absolute;left:0;right:0;top:0;bottom:0;
    z-index:0;pointer-events:none;
    background-image:radial-gradient(circle at 1px 1px,#ffffff12 1px,transparent 0);
    background-size:22px 22px;
    -webkit-mask-image:linear-gradient(to bottom,#000 0%,#000 22%,#000000c8 48%,#00000070 72%,#00000030 88%,#0000 97%);
    mask-image:linear-gradient(to bottom,#000 0%,#000 22%,#000000c8 48%,#00000070 72%,#00000030 88%,#0000 97%)}

  /* Flat — the UI is the design. */
  body[data-atmosphere="void"]{background:var(--bg);background-attachment:fixed}
  body[data-atmosphere="void"]::before{content:none}

  /* barely-there film grain so the large flat areas don't band */
  body::after{content:"";position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.035;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E")}
  body[data-atmosphere="aurora"]::after{opacity:.045}
  body[data-atmosphere="noir"]::after{opacity:.11}
  body[data-atmosphere="void"]::after{opacity:.02}
  /* 1220px is a reading measure, and this page stopped being prose a long time
     ago. On a wide screen it forced a sixty-card grid into three columns with
     390 px of empty margin either side. Prose still gets the narrow measure. */
  /* The same reasoning again, one screen size up: at 2560px the 1640px cap left
     460px empty either side and four cards to a row. 2240px is six on a 2560
     screen and five on a 1920 one; a laptop never reaches either cap. */
  .wrap{position:relative;z-index:1;max-width:2240px;margin:0 auto;padding:clamp(26px,5vw,58px) clamp(16px,3vw,30px) 90px}
  .measure{max-width:1220px}
  .hex-display{font-family:'Cinzel',Georgia,serif;letter-spacing:.1em}
  /* Ranks are set in the text face, bold, in their tier's colour beside the gem
     crest. For a while they were in Cinzel capitals, the way the client sets them,
     and it read as costume: an inscription face is slow to read at a glance, and
     sixty of them across a grid outweighed everything else on the page. The crest
     and the tier colour carry the League in a rank now; the words just have to be
     read. "Never checked" and "Unranked" stay lighter, since they are not tiers. */
  .rk-t,.rw:not(.rw-quiet) .rw-rank b,.tile:not(.rw-quiet) .t-rk,.cmp-rk:not(.q),.pal-r:not(.q){
    font-weight:700;letter-spacing:-.01em}
  /* The buttons that do something are a weight heavier than the ones that filter
     or open a menu, and that is all the type does for them. Spaced Cinzel capitals
     on every action ("REFRESH 20 STALE", "SAVE SETTINGS") were slow to read and
     loud in a row; the metal rims of the gold and teal buttons are where the client
     shows through now. */
  .cmd>button,.acts>button:not(.c-more),.acts>.btn,.mdl-f>button,#lockBtn,#bExitPreview,#bDemo,#bClearFilters,
  #sSyncGen,#sSyncPush,#sSyncPull,#sVaultSet,#sVaultRemove,#sLockNow,#fGen,#cmpClear{font-weight:600}

  /* ---- surfaces: a hairline frame, lit from above ---- */
  /* Every panel and card was framed in the accent, as metal lit from the top. With
     a gold accent that was the client; with any other it was a coloured outline
     round everything, which is half of why the page read as one colour. The frame
     is a neutral line now, a step lighter along the top, and the accent edge is
     kept for what it marks: a favourite, an open panel. */
  .hx{border-radius:var(--r-lg);padding:1px;box-shadow:var(--sh-2);
    background:linear-gradient(180deg,var(--line2),var(--line) 30%)}
  /* No backdrop-filter here, deliberately. This surface is 96–98% opaque, so at
     most 4% of whatever is behind it comes through — and what is behind it is a
     smooth background gradient, which blurring leaves essentially unchanged.
     Screenshots with and against are indistinguishable, which is the whole reason
     it goes: with sixty cards on screen it was sixty backdrop-sampling layers
     rendering an effect nobody can see. Measured honestly it is *not* a frame-rate
     win — scrolling the full grid sits at 6.9 ms a frame either way on this
     machine, no dropped frames — so the case is GPU memory and power on weaker
     hardware, not speed. Glass is kept where the surface is genuinely translucent
     and there is one of it: the sticky console, the header tray, the toast and the
     chart tooltip. */
  .hx-in{border-radius:calc(var(--r-lg) - 1px);height:100%;background:var(--panel)}
  /* A favourite has to be spottable at a glance across a full grid. The old faint
     gold edge was invisible in a row of cards, so it is a solid accent edge, a
     step brighter along the top. It used to add a warm halo and a yellowish fill
     as well, and against any accent but gold that read as a stain. */
  /* The accent edge marks an open panel. Favourite cards wore it too, and an accent
     frame round a card reads as "selected"; the star already says favourite. */
  .hx.gold{background:linear-gradient(180deg,var(--gold),color-mix(in srgb,var(--gold) 72%,var(--line)) 30%)}
  .hx.teal{background:linear-gradient(180deg,var(--teal),color-mix(in srgb,var(--teal) 62%,var(--line)) 30%)}

  /* ---- header ---- */
  .top{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:22px;margin-bottom:16px}
  /* Everything else on the page sits on a surface — cards, toolbar, stats. Left
     bare, these four buttons floated in dead space at the far edge and read as
     unrelated to each other. A tray makes them one deliberate cluster and ties
     the header to the same visual language as the rest.
     The account actions have since moved down to the console above the grid,
     where the rest of the controls over those accounts live. What's left here is
     what it should have been: two app-level utilities — settings/backup and help
     — in a quiet tray, not a second, competing command centre.
     It keeps to the right edge when the header wraps. Left-aligned on a row of its
     own it read as a stray, and the ⋯ menu, which hangs leftward from its button,
     opened off the left of a phone screen with "Export backup" cut to "ackup". */
  /* A solid plate, its edge an inner line over its own fill. The tray used to
     blur and saturate whatever was behind it inside a 1px border, and at a
     display scale of 125% or 150% a 1px line falls between screen pixels: each
     edge mixed with the boosted wash behind it by a different amount, so the
     frame came out blue along one side, faint along another and gone on a
     third. Over an opaque fill an edge can only mix with that fill. */
  .top .bar{margin-left:auto;gap:3px;padding:5px;border-radius:var(--r-md);
    background:var(--panel);box-shadow:inset 0 0 0 1px #ffffff12,var(--sh-1);
    /* lifted above what follows, so the ⋯ menu inside it paints over the page */
    position:relative;z-index:30}
  .tray-sep{width:1px;align-self:stretch;margin:2px 3px;flex-shrink:0;
    background:linear-gradient(180deg,transparent,var(--line2),transparent)}
  /* The old header stacked a pill, then the title, then a rule — three full-width
     bands of decreasing importance, all left-aligned, which is what made it read
     as flat and pasted into the corner. This is a lockup instead: a mark the
     wordmark sits against, with the descriptor tucked under it. */
  /* The whole vault's spread across the ladder, in the band the header was
     wasting. The dashboard's gem row already answers "how many are Platinum";
     this answers what shape the collection is, and answers it without being
     opened. Best on the left, the same order the list is sorted in. */
  .ribbon{flex:1 1 260px;min-width:0;max-width:640px;display:flex;flex-direction:column;gap:6px}
  .ribbon:empty{display:none}
  .rib-bar{display:flex;gap:2px;height:16px}
  .rib-seg{flex-grow:1;flex-basis:0;min-width:20px;padding:0;border:0;border-radius:2px;
    /* the tier's own colour. Mixed 62% into the near-black page it came out as the
       muddy version of every tier: Gold as olive, Silver as slate, Platinum as
       a dull teal, which is what made the top of the page read as murky */
    background:var(--c);box-shadow:none;
    display:flex;align-items:center;justify-content:center;overflow:hidden;
    transform:none;transition:background .16s ease,box-shadow .16s ease,filter .16s ease}
  /* The states that are not rungs on the ladder: sized to their own number so
     they cannot take the bar over, and set slightly back so the ladder reads
     first. */
  .rib-seg.tail{flex:0 0 auto;padding:0 10px;min-width:28px;opacity:.82}
  .rib-seg.tail:hover,.rib-seg.tail.on{opacity:1}
  .rib-seg:first-child{border-radius:var(--r-xs) 2px 2px var(--r-xs)}
  .rib-seg:last-child{border-radius:2px var(--r-xs) var(--r-xs) 2px}
  .rib-seg:only-child{border-radius:var(--r-xs)}
  .rib-seg i{font-style:normal;font-size:calc(10px*var(--ts));font-weight:700;letter-spacing:.02em;
    color:var(--bg);opacity:.72;font-variant-numeric:tabular-nums}
  .rib-seg:hover{background:var(--c);transform:none;border:0;filter:brightness(1.06)}
  .rib-seg:active{transform:none}
  .rib-seg:hover i{opacity:.95}
  /* the selected tier reads as the only lit one, so the bar doubles as a readout
     of which filter is on */
  .rib-bar:has(.rib-seg.on) .rib-seg:not(.on){background:color-mix(in srgb,var(--c) 22%,var(--bg))}
  .rib-bar:has(.rib-seg.on) .rib-seg:not(.on) i{opacity:.4}
  .rib-seg.on{background:var(--c);box-shadow:0 0 0 1px var(--bg),0 0 14px -2px var(--c)}
  .rib-cap{display:flex;justify-content:space-between;gap:3px 12px;flex-wrap:wrap;
    font-size:calc(11px*var(--ts));letter-spacing:.02em;color:var(--faint)}
  .rib-cap b{font-weight:600;color:var(--muted)}
  .brand{display:flex;align-items:center;gap:16px;min-width:0}
  /* A gem set in a turned gold frame, rather than a rounded app-icon tile. */
  /* Flat and still, like everything round it: a solid stone in a two-line frame.
     It was a glossy gem that glowed and pulsed, which is the one sparkle the eye
     went to first and the reason the rest of the header looked dull beside it. */
  .brand-mark{position:relative;width:44px;height:44px;margin:5px;flex-shrink:0;border-radius:var(--r-xs);
    display:flex;align-items:center;justify-content:center;transform:rotate(45deg);
    background:var(--panel2);
    box-shadow:inset 0 0 0 1px var(--gold),0 0 0 3px var(--bg),0 0 0 4px color-mix(in srgb,var(--gold) 45%,var(--line))}
  .brand-mark::before{content:"";width:18px;height:18px;border-radius:1px;background:var(--gold)}
  .brand-sub{display:flex;align-items:center;gap:8px;margin-top:6px;flex-wrap:wrap;
    font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--faint)}
  .brand-sub b{font-weight:600;letter-spacing:.14em;color:var(--gold-txt);padding:3px 9px;border-radius:var(--r-xs);
    background:color-mix(in srgb,var(--gold) 11%,transparent);
    border:1px solid color-mix(in srgb,var(--gold) 24%,transparent)}
  /* Solid, in the accent's text colour. It was chrome: a sweep of light across
     the letters, which next to the flat type and the flat rank bar beside it read
     as a different material, and in a blue accent as blue foil. The display face
     is what carries the brand. */
  h1,.lock-wm{margin:0;font-weight:700;font-size:calc(clamp(29px,4.2vw,41px)*var(--ts));line-height:1.04;
    color:var(--gold-txt)}
  h1.hex-display,.lock-wm{letter-spacing:.02em}
  /* Replaces the full-width rule that used to sit here. That line started hard and
     faded into nothing, belonging to neither the header above nor the text below —
     a hairline down the side of the paragraph does the same separating job while
     actually pointing at what it introduces. */
  .sub{font-size:calc(13px*var(--ts));color:var(--muted);margin:22px 0 30px;max-width:640px;line-height:1.65;
    padding-left:15px;border-left:2px solid transparent;
    border-image:linear-gradient(180deg,color-mix(in srgb,var(--gold) 55%,transparent),transparent 88%) 1}

  /* ---- buttons ---- */
  /* One material for every button: a plate in satin (--sheen). The accent ones
     are the same plate in the accent's colour, and Login is the same again filled;
     none adds a gloss or a glow of its own. Variants set background-COLOR only,
     because the shorthand would wipe the sheen; a button that is not a plate
     (an icon, a link, a menu row) clears it with background:none and box-shadow:none. */
  button,.btn{font:inherit;font-size:calc(13px*var(--ts));font-weight:500;cursor:pointer;line-height:1.2;white-space:nowrap;
    border:1px solid var(--edge);border-radius:var(--r-sm);background-color:var(--panel2);background-image:var(--sheen);
    color:var(--text);box-shadow:var(--sh-btn);
    padding:8px 13px;display:inline-flex;align-items:center;justify-content:center;gap:7px;
    transition:background .16s ease,border-color .16s ease,color .16s ease,box-shadow .16s ease,transform .09s ease}
  button:hover,.btn:hover{background-color:var(--panel3);border-color:var(--line2)}
  button:active{transform:translateY(1px)}
  button:disabled{opacity:.4;cursor:not-allowed;transform:none}
  :focus-visible{outline:2px solid var(--teal-txt);outline-offset:2px}
  /* text fields and selects draw their own indicator further down; checkboxes
     and the colour swatch keep this outline so keyboard focus stays visible */
  input:not([type=checkbox]):not([type=color]):focus,select:focus,textarea:focus{outline:none}
  /* The accent's own buttons: the same plate as every other, edged and lettered
     in the accent and tinted with it. They used to be metal: a gradient rim lit at
     the top, a glow underneath that grew on hover, which beside the flat buttons
     next to them read as chrome next to matte. Light text on a tinted plate
     rather than dark on a fill, because the accent is the user's to pick. */
  /* One colour for every main button: the secondary (action) colour, which Settings
     has always described as "Login, Refresh, Add". Check all, Save, Unlock, Add all
     and the rest wore the primary instead, so a toolbar showed two "this is the
     button" colours side by side. The primary keeps the title, favorites and what
     is selected. */
  .b-teal{color:var(--teal-txt);border-color:color-mix(in srgb,var(--teal) 60%,var(--edge));
    background-color:color-mix(in srgb,var(--teal) 9%,var(--panel2))}
  .b-teal:hover{color:var(--text);border-color:var(--teal);background-color:color-mix(in srgb,var(--teal) 17%,var(--panel2))}
  .b-danger{color:var(--danger)}
  .b-danger:hover{color:var(--danger);border-color:color-mix(in srgb,var(--danger) 45%,transparent);
    background-color:color-mix(in srgb,var(--danger) 13%,var(--panel2))}
  /* utilities, not actions — no rim of their own inside the tray */
  #bMore,#bHelp{width:34px;padding:8px 0;color:var(--muted);font-size:calc(16px*var(--ts));
    background:none;border-color:transparent;box-shadow:none}
  #bMore:hover,#bHelp:hover{color:var(--text);background:#ffffff12;border-color:transparent}
  .bar{display:flex;gap:9px;flex-wrap:wrap;align-items:center}

  /* ---- dropdown menu ---- */
  #moreMenu button{width:100%;justify-content:flex-start;padding:8px 11px;border-radius:var(--r-sm);
    background:none;border-color:transparent;box-shadow:none;color:var(--text);font-weight:450}
  #moreMenu button:hover{background:#ffffff0f;border-color:transparent;color:var(--text)}
  #moreMenu button.b-danger{color:var(--danger)}
  #moreMenu button.b-danger:hover{background:color-mix(in srgb,var(--danger) 14%,transparent)}

  /* ---- native controls ---- */
  input[type=checkbox]{padding:0;border:0;background:none;accent-color:var(--teal);cursor:pointer;box-shadow:none}
  input[type=checkbox]:hover{border:0;background:none}
  input[type=color]{padding:3px;cursor:pointer;border-radius:var(--r-sm)}
  input[type=search]{-webkit-appearance:none;appearance:none}
  input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;height:14px;width:14px;cursor:pointer;
    background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b93a6' stroke-width='2.4' stroke-linecap='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'/%3E%3Cline x1='6' y1='6' x2='18' y2='18'/%3E%3C/svg%3E") center/contain no-repeat}
  code{font-family:ui-monospace,'SF Mono',Consolas,monospace;font-size:.92em;padding:2px 6px;border-radius:var(--r-xs);
    background:#ffffff0f;border:1px solid #ffffff21;color:var(--text)}

  /* ---- banners ---- */
  .banner{display:none;border:1px solid color-mix(in srgb,var(--danger) 45%,transparent);color:var(--danger);
    background:color-mix(in srgb,var(--danger) 9%,var(--panel));padding:12px 16px;font-size:calc(13px*var(--ts));margin-bottom:18px;border-radius:var(--r-md)}
  .banner.show{display:block}
  .bk-note{display:flex;align-items:center;gap:8px;margin:-4px 0 16px;font-size:calc(12px*var(--ts));color:var(--muted)}
  .bk-note .ico{color:var(--faint)}
  .bk-note button{padding:3px 9px;font-size:calc(12px*var(--ts));font-weight:600;color:var(--gold-txt);background:none;
    border-color:color-mix(in srgb,var(--gold) 30%,transparent);box-shadow:none}
  .setup{margin:-2px 0 16px}
  .setup-in{display:flex;align-items:center;gap:14px;flex-wrap:wrap;padding:10px 14px}
  .setup-k{font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-txt)}
  .setup-k em{font-style:normal;color:var(--faint);letter-spacing:.04em;margin-left:6px}
  .setup-steps{display:flex;gap:8px;flex-wrap:wrap;margin:0;padding:0;list-style:none;flex:1}
  .setup-steps li{display:flex;align-items:center;gap:6px;font-size:calc(13px*var(--ts))}
  .setup-steps li.done{color:var(--faint);padding:6px 4px}
  .setup-steps li.done .ico{color:var(--good)}
  .setup-steps li.done span{text-decoration:line-through;text-decoration-color:color-mix(in srgb,var(--faint) 60%,transparent)}
  .setup-steps button{padding:6px 11px;font-size:calc(13px*var(--ts));font-weight:600}
  .setup-steps button b{display:inline-grid;place-items:center;width:18px;height:18px;flex-shrink:0;margin-right:2px;border-radius:50%;
    font-size:calc(11px*var(--ts));color:var(--bg);background:var(--gold-mid)}
  .setup-x{padding:4px 8px;font-size:calc(12px*var(--ts));color:var(--faint);background:none;border-color:transparent;box-shadow:none}
  .setup-x:hover{color:var(--text);background:none;border-color:transparent}
  @media (max-width:560px){
    .setup-in{position:relative;gap:10px}
    .setup-x{position:absolute;top:6px;right:6px}
    .setup-steps{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));flex-basis:100%;gap:6px}
    /* two columns leave "Set a master password" no room on a phone: it wraps
       onto a second line rather than running into the button's edge */
    .setup-steps button{width:100%;justify-content:flex-start;padding:7px 9px;font-size:calc(12px*var(--ts));
      white-space:normal;text-align:left;line-height:1.25;min-height:36px}
    .setup-steps li.done{font-size:calc(12px*var(--ts))}
  }
  .bk-note button:hover{background:color-mix(in srgb,var(--gold) 10%,transparent);border-color:color-mix(in srgb,var(--gold) 55%,transparent)}

  /* ---- dashboard ---- */
  .dash{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr))}
  .stat{padding:16px 20px;position:relative;overflow:hidden;min-width:0}
  .dash[style*="--cols"]{grid-template-columns:repeat(var(--cols),minmax(0,1fr))}
  .stat+.stat::before{content:"";position:absolute;left:0;top:15px;bottom:15px;width:1px;background:var(--line)}
  .stat.rs::before{display:none}
  .stat .k{font-size:calc(11px*var(--ts));font-weight:600;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);margin-bottom:7px}
  .stat .v{font-size:calc(20px*var(--ts));font-weight:600;color:var(--text);line-height:1.25;letter-spacing:-.015em;font-variant-numeric:tabular-nums}
  /* nowrap, or a long enough figure breaks between the number and its unit and
     leaves "WR" alone on the next line. It wraps as one piece instead. */
  .stat .v small{font-size:calc(12px*var(--ts));color:var(--muted);font-weight:400;letter-spacing:0;white-space:nowrap}
  .stat .v.nw{white-space:nowrap}
  .stat .v.nw small{margin-left:1px}
  .stat .v.spark-wrap{font-size:calc(12px*var(--ts));line-height:1.2;overflow:visible;max-width:100%;width:100%;position:relative}
  /* capped: stretched 28px tall across a 400px cell, +246 LP drew as a flat line */
  .pool-spark{display:block;width:100%;max-width:220px;height:28px;margin-top:4px;opacity:.85;overflow:visible;cursor:crosshair}
  .pool-spark circle.pool-dot{fill:var(--teal);stroke:var(--panel);stroke-width:1.5;pointer-events:none}
  .pool-spark .pool-hit{fill:transparent;stroke:none;cursor:pointer}
  .spark-tip{
    position:fixed;z-index:80;pointer-events:none;
    padding:6px 9px;border-radius:var(--r-sm);font-size:calc(12px*var(--ts));line-height:1.35;
    color:var(--text);background:var(--panel3);border:1px solid var(--line2);
    box-shadow:0 8px 24px #00000088;max-width:220px;white-space:pre-line;
    opacity:0;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease;
  }
  .spark-tip.on{opacity:1;transform:none}
  .dash-toggle{display:flex;justify-content:flex-end;margin-bottom:14px}
  .ghost-toggle{font-size:calc(12px*var(--ts));padding:6px 13px;border-radius:var(--r-xs);color:var(--muted);
    background-color:#ffffff09;border:1px solid var(--edge)}
  .ghost-toggle:hover{background-color:#ffffff14;border-color:var(--line2);color:var(--text)}
  .ghost-toggle .ico{transition:transform .22s cubic-bezier(.22,1,.36,1)}
  .ghost-toggle.open .ico{transform:rotate(180deg)}
  .dash-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;padding:9px 14px 0}
  .dash-filter{display:flex;align-items:center;gap:9px;flex-wrap:wrap}
  .dash-filter select{font-size:calc(12px*var(--ts));padding:5px 28px 5px 10px;background-color:#ffffff08}
  .dash-ex{font-size:calc(11px*var(--ts));color:var(--faint)}
  /* Was a bare name, a percentage and a hanging "30G" all at different sizes with
     nothing holding them together — at eight across a row it read as debris. One
     pill per champion, with the games as a W-L pair rather than a count needing a
     unit letter glued to it. */
  .gem.cmb{gap:7px;padding:3px 4px 3px 10px;border-radius:var(--r-xs);
    background:#ffffff08;border:1px solid #ffffff1c}
  .gem.cmb .n{font-weight:500}
  /* flex, not block. A <button> centres its content box vertically, so the two
     tiles that are buttons sat 24 and 31 px lower than the five that are not —
     three baselines in one row of seven, which reads as a broken grid. */
  /* gap:0 — the generic button rule gives every button a 7px flex gap, which on
     top of the label's own margin dropped these tiles' figures 7px below the rest */
  .stat-b{display:flex;flex-direction:column;align-items:stretch;justify-content:flex-start;gap:0;
    width:100%;text-align:left;border:0;border-radius:0;background:none;box-shadow:none;
    padding:16px 20px;font:inherit;white-space:normal}
  .stat-b:hover{background:#ffffff08;border:0}
  .stat-b.on{background:color-mix(in srgb,var(--gold) 11%,transparent);border:0;
    box-shadow:inset 2px 0 0 var(--gold)}
  .s-note{font-size:calc(11px*var(--ts));color:var(--faint);margin-top:4px;line-height:1.4}
  /* A "·" list (dotList) breaks between its pieces, never inside one, and never
     leaves a dot at either end of a line: each dot sits in the gap before its
     piece, so a piece that starts a line has its dot out past the left edge,
     where the clip hides it. The dots stay real text for copying and tests. */
  .dots{display:inline-flex;max-width:100%;clip-path:inset(-.5em -.5em -.5em 0)}
  .dots-in{display:flex;flex-wrap:wrap;min-width:0;margin-left:-.8em}
  .dot-i{position:relative;margin-left:.8em;white-space:nowrap}
  .dot-s{position:absolute;right:100%;width:.8em;text-align:center;font-style:normal;white-space:pre}
  .gem.cmb b{font-weight:700;color:var(--muted);font-variant-numeric:tabular-nums}
  .gem.cmb b.wr-good{color:var(--good)}
  .gem.cmb i.g{font-style:normal;font-size:calc(11px*var(--ts));font-weight:600;color:var(--muted);
    font-variant-numeric:tabular-nums;padding:2px 8px;border-radius:var(--r-xs);background:#ffffff12}
  .gem.cmb i.g em{font-style:normal;opacity:.5;padding:0 1px}
  .dist{display:flex;gap:13px;flex-wrap:wrap;align-items:center;padding:13px 20px;position:relative}
  /* The client divides with a hairline that is brightest in the middle and a
     small diamond set on it, not with a flat grey rule. In the accent it was one
     more coloured line among the frames; the shape is the ornament, so the line
     is neutral. */
  .hx-in>.dist{border-top:0!important}
  .hx-in>.dist::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;pointer-events:none;
    background:linear-gradient(90deg,transparent,var(--line) 18%,var(--line2) 50%,var(--line) 82%,transparent)}
  .hx-in>.dist::after{content:"";position:absolute;left:50%;top:-3px;width:5px;height:5px;pointer-events:none;
    transform:translateX(-50%) rotate(45deg);background:var(--panel);border:1px solid var(--line2)}
  .dist .k{font-size:calc(11px*var(--ts));font-weight:600;text-transform:uppercase;letter-spacing:.14em;color:var(--muted);margin-right:2px}
  .hx-rule{position:relative;height:1px;width:100%;flex-shrink:0;
    background:linear-gradient(90deg,transparent,var(--line) 18%,var(--line2) 50%,var(--line) 82%,transparent)}
  .hx-rule::after{content:"";position:absolute;left:50%;top:-3px;width:5px;height:5px;
    transform:translateX(-50%) rotate(45deg);background:var(--panel);border:1px solid var(--line2)}
  .gem{display:inline-flex;align-items:center;gap:7px;font-size:calc(12px*var(--ts));color:var(--text)}
  /* the tier lozenge — scoped to a class rather than the bare tag, because every
     <i> inside a .gem was inheriting a 9px box and a 45° rotation, which is what
     turned the champion pills' W-L into a squashed diagonal smudge */
  .gem i.d{width:9px;height:9px;border-radius:3px;transform:rotate(45deg);display:inline-block}

  /* ---- console: the account actions and the filters, as one control ---- */
  /* Check all ranks and + Add account used to sit in the header, diagonally
     opposite the grid they act on, while every other control over the same
     accounts lived down here. Joined onto the filter bar they read as what they
     are — the row you work the collection from — and being sticky as one unit
     means the primary action stays reachable instead of scrolling off the top. */
  .console{position:sticky;top:0;z-index:10;margin:0 0 18px;
    border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;
    /* glass, because it rides over the cards: blurred, but not saturated, which
       turned whatever scrolled under it into a smear of its brightest colour */
    background:color-mix(in srgb,var(--panel) 92%,transparent);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
    box-shadow:var(--sh-1)}
  .cmd{display:flex;gap:9px;flex-wrap:wrap;align-items:center;padding:11px 12px;
    border-bottom:1px solid var(--line);background:linear-gradient(180deg,#ffffff08,transparent)}
  /* the count was a lone grey line under the grid, where it answered a question
     nobody was asking by then; beside the buttons it's a status readout */
  .cmd-r{margin-left:auto;display:flex;align-items:center;gap:9px;min-width:0}
  .cmd-count{padding-right:3px;font-size:calc(12px*var(--ts));color:var(--faint);
    display:inline-flex;align-items:center;gap:9px;letter-spacing:.015em}
  .cmd-count b{font-weight:600;color:var(--muted)}
  /* Out of date is "worth a look", not lost: amber, the warning colour, like
     every other state that a click puts right (see --warn). In red it was the
     loudest thing on the page in any vault left alone for a weekend. */
  .cmd-count .warn{color:var(--warn-txt);font-weight:600;font-size:calc(12px*var(--ts));letter-spacing:.015em;
    padding:3px 9px;border-radius:var(--r-xs);background:none;border:1px solid transparent;box-shadow:none}
  .cmd-count .warn:hover{background:color-mix(in srgb,var(--warn) 13%,transparent);
    border-color:color-mix(in srgb,var(--warn) 34%,transparent)}
  .cmd-count .warn.on{background:color-mix(in srgb,var(--warn) 18%,transparent);
    border-color:color-mix(in srgb,var(--warn) 46%,transparent)}
  .tools{display:flex;gap:9px;flex-wrap:wrap;align-items:center;padding:12px}
  /* Scrolled past the header the console is pure overhead on screen, so it gives
     some of that height back. Controls stay put — only the air around them goes. */
  body.scrolled .cmd{padding:7px 12px}
  body.scrolled .tools{padding:8px 12px}
  body.scrolled .console{border-radius:0 0 var(--r-md) var(--r-md)}
  .cmd,.tools{transition:padding .18s cubic-bezier(.22,1,.36,1)}
  @media (prefers-reduced-motion:reduce){.cmd,.tools{transition:none}}
  /* layout switch: segmented control. Kept fully isolated from the global
     button chrome — that rule transitions border-color + translateY on hover,
     which made the dividers flash and the icons look like they squeezed. */
  .seg{display:inline-flex;align-items:stretch;border:1px solid var(--edge);border-radius:var(--r-sm);
    overflow:hidden;background:var(--sheen),var(--panel2);box-shadow:var(--sh-btn);flex-shrink:0}
  .seg button{
    box-sizing:border-box;width:36px;height:32px;padding:0;margin:0;gap:0;
    border:0;border-radius:0;border-color:transparent;
    background:transparent;color:var(--faint);
    box-shadow:none;transform:none;outline:none;-webkit-tap-highlight-color:transparent;
    /* only paint/color — never border or transform (those inherit from button{}) */
    transition:background .16s ease,color .16s ease;
  }
  /* Dividers via inset shadow so global button:hover { border-color } cannot recolor them */
  .seg button+button{box-shadow:inset 1px 0 0 var(--line);border-left:0}
  .seg button:hover,.seg button:focus-visible{
    background:#ffffff0d;border:0;border-color:transparent;color:var(--muted);
    box-shadow:none;transform:none;outline:none;
  }
  .seg button+button:hover,.seg button+button:focus-visible{
    box-shadow:inset 1px 0 0 var(--line);
  }
  .seg button:active,.seg button:focus{border:0;border-color:transparent;box-shadow:none;transform:none;outline:none}
  .seg button+button:active,.seg button+button:focus{box-shadow:inset 1px 0 0 var(--line)}
  .seg button.on{
    background:color-mix(in srgb,var(--gold) 20%,transparent);color:var(--gold-txt);
    border:0;border-color:transparent;box-shadow:none;transform:none;
  }
  .seg button+button.on{box-shadow:inset 1px 0 0 var(--line)}
  .seg button.on:hover,.seg button.on:focus-visible{
    background:color-mix(in srgb,var(--gold) 28%,transparent);color:var(--gold-txt);
    border:0;transform:none;outline:none;
  }
  .seg button+button.on:hover,.seg button+button.on:focus-visible{box-shadow:inset 1px 0 0 var(--line)}
  /* keyboard focus stays inside the segment — outer :focus-visible outline was
     clipped by overflow:hidden and read as a flashing border */
  .seg button:focus-visible{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--teal) 55%,transparent)}
  .seg button+button:focus-visible{
    box-shadow:inset 1px 0 0 var(--line),inset 0 0 0 1px color-mix(in srgb,var(--teal) 55%,transparent);
  }
  .seg .ico{width:15px;height:15px;display:block;flex:0 0 15px;margin:0;pointer-events:none;
    overflow:visible;vertical-align:unset}
  /* Atmosphere picker: six little windows. Tip sits in the tile under the name
     (not a floating box) — absolute tips overflowed the settings body by a few
     pixels and painted a useless horizontal scrollbar, and they also sat on top
     of neighbouring labels. */
  .atm-block{margin-top:18px}
  .atm-block .atm-h,.grp>.atm-h{font-size:calc(12px*var(--ts));font-weight:600;color:var(--text);margin-bottom:8px}
  /* themes: two colours side by side is the whole idea, so that is the tile */
  .thm-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
  .thm{display:flex;flex-direction:column;align-items:stretch;gap:6px;padding:6px 6px 7px;min-width:0;
    border:1px solid var(--edge);border-radius:var(--r-sm);background:var(--panel2);box-shadow:none;
    color:var(--muted);font-size:calc(12px*var(--ts));font-weight:600;text-align:left}
  .thm:hover{background:var(--panel3);border-color:var(--line2);color:var(--text)}
  .thm.on{border-color:color-mix(in srgb,var(--gold) 55%,var(--line2));color:var(--gold-txt);
    background:color-mix(in srgb,var(--gold) 10%,var(--panel2));box-shadow:0 0 0 1px color-mix(in srgb,var(--gold) 28%,transparent)}
  .thm:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
  /* No border: a background repeats under its own border, so each end showed a
     sliver of the other colour. The edge is an inset shadow drawn over it instead. */
  .thm-sw{display:block;height:24px;border-radius:var(--r-xs);border:0;box-shadow:inset 0 0 0 1px #ffffff1f;pointer-events:none}
  .thm-n{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}
  .thm-n small{display:block;font-size:calc(10.5px*var(--ts));font-weight:500;color:var(--faint);white-space:normal;line-height:1.3}
  .atm-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}
  @media(max-width:520px){.atm-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
  .atm-tile{position:relative;display:flex;flex-direction:column;align-items:stretch;gap:5px;
    padding:7px;border:1px solid var(--edge);border-radius:var(--r-sm);background:var(--panel2);
    color:var(--muted);font-size:calc(12px*var(--ts));font-weight:600;text-align:left;box-shadow:none;
    overflow:hidden;min-width:0;
    transition:border-color .16s ease,background .16s ease,color .16s ease,box-shadow .16s ease}
  .atm-tile:hover{background:var(--panel3);border-color:var(--line2);color:var(--text)}
  .atm-tile.on{border-color:color-mix(in srgb,var(--gold) 55%,var(--line2));
    background:color-mix(in srgb,var(--gold) 10%,var(--panel2));color:var(--gold-txt);
    box-shadow:0 0 0 1px color-mix(in srgb,var(--gold) 28%,transparent)}
  .atm-tile:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
  /* the edge is an inset shadow, not a border, for the same reason as .thm-sw:
     the glow at the top repeated under a border and showed along the bottom */
  .atm-mini{display:block;height:46px;border-radius:var(--r-xs);border:0;box-shadow:inset 0 0 0 1px #ffffff12;
    background:var(--bg);overflow:hidden;position:relative;pointer-events:none;flex-shrink:0}
  .atm-mini::after{content:"";position:absolute;inset:0;opacity:.04;pointer-events:none;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E")}
  .atm-mini[data-atm="spotlight"]{background:
    radial-gradient(60% 90% at 50% -12%,color-mix(in srgb,var(--gold) 20%,transparent),transparent 72%),
    radial-gradient(90% 100% at 50% -20%,#ffffff12,transparent 64%),
    var(--bg)}
  .atm-mini[data-atm="aurora"]{background:
    radial-gradient(ellipse 70% 90% at 5% 0%,color-mix(in srgb,var(--gold) 38%,transparent),transparent 70%),
    radial-gradient(ellipse 65% 85% at 100% 10%,color-mix(in srgb,var(--teal) 34%,transparent),transparent 68%),
    var(--bg)}
  .atm-mini[data-atm="noir"]{background:radial-gradient(ellipse 70% 60% at 50% 45%,#12141a 0%,#050608 100%)}
  .atm-mini[data-atm="noir"]::after{opacity:.12}
  .atm-mini[data-atm="lattice"]{background-color:var(--bg);
    background-image:
      radial-gradient(80% 70% at 50% -20%,color-mix(in srgb,var(--gold) 18%,transparent),transparent 60%),
      linear-gradient(#ffffff14 1px,transparent 1px),
      linear-gradient(90deg,#ffffff14 1px,transparent 1px);
    background-size:auto,14px 14px,14px 14px}
  .atm-mini[data-atm="stardust"]{background-color:var(--bg);
    background-image:
      radial-gradient(70% 60% at 20% -10%,color-mix(in srgb,var(--gold) 22%,transparent),transparent 55%),
      radial-gradient(circle at 1px 1px,#ffffff30 1px,transparent 0);
    background-size:auto,8px 8px}
  .atm-mini[data-atm="void"]{background:var(--bg)}
  .atm-mini[data-atm="void"]::after{opacity:.02}
  .atm-name{padding:1px 2px 0;line-height:1.35}
  /* In-flow tip with height/opacity instead of display:none — display cannot
     ease, so the old one snapped on and off. Keep the fade, but open quickly:
     a long delay felt like lag. */
  .atm-tip{max-height:0;opacity:0;overflow:hidden;margin-top:-5px;padding:0 2px;
    color:var(--faint);font-size:calc(11px*var(--ts));font-weight:450;line-height:1.4;
    pointer-events:none;
    transition:max-height .22s cubic-bezier(.22,1,.36,1),opacity .18s ease,margin-top .18s ease;
    transition-delay:.06s}
  .atm-tile:hover .atm-tip,.atm-tile:focus-visible .atm-tip{
    max-height:4.8em;opacity:1;margin-top:0;padding:0 2px 1px;
    transition-delay:.04s}
  @media (prefers-reduced-motion:reduce){
    .atm-tip{transition:none;transition-delay:0s}
  }
  /* A run over sixty accounts is minutes long. Where it has got to, which account
     it is on, and how to stop it all have to be on screen the whole time. */
  .runbar{position:relative;height:26px;display:flex;align-items:center;
    border-top:1px solid var(--line);background:#00000030;overflow:hidden}
  .run-fill{position:absolute;left:0;top:0;bottom:0;width:0;
    background:linear-gradient(90deg,color-mix(in srgb,var(--gold) 34%,transparent),color-mix(in srgb,var(--gold) 16%,transparent));
    transition:width .3s cubic-bezier(.22,1,.36,1)}
  .run-txt{position:relative;padding:0 13px;font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.02em;color:var(--gold-txt);
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  /* failures used to be a toast telling you to go and find them among sixty cards */
  .failbar{display:flex;align-items:center;gap:11px;flex-wrap:wrap;padding:9px 13px;
    border-top:1px solid color-mix(in srgb,var(--danger) 30%,transparent);
    background:color-mix(in srgb,var(--danger) 9%,transparent);font-size:calc(12px*var(--ts))}
  .fb-n{font-weight:600;color:var(--danger)}
  .fb-l{color:var(--muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
  .failbar button{font-size:calc(12px*var(--ts));padding:5px 10px}
  .fb-x{padding:5px 9px;background:none;border-color:transparent;box-shadow:none;color:var(--faint)}
  /* what the stats panel just filtered on — invisible filters make a short grid
     look like a bug */
  .chips{display:flex;gap:7px;flex-wrap:wrap;padding:0 12px}
  .chips:not(:empty){padding:11px 12px;border-top:1px solid var(--line)}
  .fchip{font-size:calc(12px*var(--ts));font-weight:600;padding:4px 8px 4px 11px;gap:7px;border-radius:var(--r-xs);
    color:var(--gold-txt);border-color:color-mix(in srgb,var(--gold) 45%,transparent);
    background:color-mix(in srgb,var(--gold) 12%,transparent)}
  .fchip span{opacity:.65;font-size:calc(10px*var(--ts))}
  .fchip:hover{color:var(--gold-txt);background:color-mix(in srgb,var(--gold) 22%,transparent);
    border-color:color-mix(in srgb,var(--gold) 70%,transparent)}
  .fchip-all{color:var(--faint);background:none;border-color:var(--edge);box-shadow:none;padding:4px 11px}
  .fchip-all:hover{color:var(--text);background:var(--panel3);border-color:var(--line2)}
  input,select,textarea{font:inherit;font-size:calc(14px*var(--ts));background:var(--panel2);border:1px solid var(--edge);
    border-radius:var(--r-sm);color:var(--text);padding:9px 12px;
    transition:border-color .16s ease,box-shadow .16s ease,background .16s ease}
  input:hover,select:hover,textarea:hover{border-color:var(--line2)}
  /* Text fields get the full treatment — you're about to type, so the ring
     marks where your keystrokes land and it earns its weight.
     Selects get a border tint only. A <select> keeps focus after you pick an
     option, and browsers deliberately count that as focus-visible (arrow keys
     still change the value), so :focus-visible can't tell a mouse pick from a
     keyboard one here. Anything heavier than a tint therefore sits on the
     control lit up until you happen to click elsewhere, which reads as stuck.
     A tint still shows keyboard users where they are without shouting. */
  input:not([type=checkbox]):not([type=color]):focus,textarea:focus{
    border-color:color-mix(in srgb,var(--teal) 60%,transparent);
    box-shadow:0 0 0 3px color-mix(in srgb,var(--teal) 13%,transparent);background-color:var(--panel3)}
  select:focus{border-color:color-mix(in srgb,var(--teal) 55%,transparent)}
  textarea{resize:vertical;line-height:1.55}
  /* A dropdown is a button that opens a list, so it wears the buttons' satin;
     a text field is a well you type into and stays flat. */
  select{appearance:none;-webkit-appearance:none;padding-right:32px;cursor:pointer;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238b93a6' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"),var(--sheen);background-repeat:no-repeat;background-position:right 10px center,0 0;background-size:13px,100% 100%;box-shadow:var(--sh-btn)}
  select:hover,select:focus{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23e9edf5' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"),var(--sheen)}
  input::placeholder,textarea::placeholder{color:var(--faint)}
  .tools input[type=search]{flex:1;min-width:180px}
  /* Favorites and Archived are toggles, so their lit state is a class carrying
     aria-pressed with it — it used to be two inline style properties assigned by
     hand from three different places, which is how the state got out of step with
     the filter it stands for. */
  .tools button.on{color:var(--star);border-color:color-mix(in srgb,var(--star) 55%,transparent);
    background:color-mix(in srgb,var(--star) 12%,transparent)}
  .tools button.on:hover{color:var(--star);border-color:color-mix(in srgb,var(--star) 80%,transparent);
    background:color-mix(in srgb,var(--star) 20%,transparent)}
  /* Tags are one more filter button in the tools row (display:contents puts the
     handle straight into the row's flex), and the list, when open, takes a line of
     its own under it. It used to be a tab hanging off the console's bottom edge,
     which read as a layout bug; on a phone it folds behind Filters with the rest. */
  .tagbar{display:contents}
  .tagbar:empty{display:none}
  /* Gold on this handle means one thing: a tag is filtering. It used to mean two —
     the resting state also had a gold count on a gold wash inside a gold-washed
     tab, so "off" and "on" were the same picture at two strengths and a vault with
     one tag and no filter looked like a filter was on. Only the mark keeps a tint
     at rest, because a glyph is identity and a lit pill full of a number is status.
     Everything else waits for .lit. */
  .tag-handle{gap:7px;transition:color .16s ease,border-color .16s ease,background .16s ease}
  .tag-handle:hover{color:var(--text);border-color:color-mix(in srgb,var(--gold) 38%,var(--line2));
    background:linear-gradient(180deg,color-mix(in srgb,var(--gold) 17%,var(--panel2)),var(--panel2))}
  /* how many tags the vault has — a fact, not a filter, so it is not gold until one is */
  .tag-handle b{font-weight:700;letter-spacing:.02em;font-variant-numeric:tabular-nums;
    font-size:calc(10px*var(--ts));line-height:1.5;padding:0 6px;border-radius:var(--r-xs);
    color:var(--muted);background:#ffffff12;transition:color .16s ease,background .16s ease}
  .tag-handle:hover b{color:var(--text);background:#ffffff1f}
  .tag-ico{color:color-mix(in srgb,var(--gold) 42%,var(--muted));transition:color .16s ease}
  .tag-handle:hover .tag-ico,.tag-handle.lit .tag-ico{color:var(--gold-txt)}
  /* lit while a tag is filtering — the chip that says which one lives in the
     console's filter row, this only says "one is on" */
  .tag-handle.lit{color:var(--gold-txt);border-color:color-mix(in srgb,var(--gold) 50%,transparent);
    background:color-mix(in srgb,var(--gold) 15%,var(--panel2))}
  .tag-handle.lit b,.tag-handle.lit:hover b{color:var(--gold-txt);background:color-mix(in srgb,var(--gold) 34%,transparent)}
  .tag-handle .ico{width:11px;height:11px}
  .tag-chev{transition:transform .22s cubic-bezier(.22,1,.36,1);opacity:.75}
  .tag-handle:hover .tag-chev{opacity:1}
  .tag-handle.open .tag-chev{transform:rotate(180deg)}
  .tag-list{display:flex;gap:7px;flex-wrap:wrap;flex-basis:100%;padding-top:2px;order:99} /* last, on a line of its own */
  @media (prefers-reduced-motion:reduce){.tag-handle,.tag-handle .ico{transition:none}}
  .chip{font-size:calc(12px*var(--ts));font-weight:500;padding:5px 12px;border:1px solid var(--edge);border-radius:var(--r-xs);
    background:var(--panel2);color:var(--muted);cursor:pointer;
    transition:background .15s ease,border-color .15s ease,color .15s ease}
  .chip:hover{color:var(--text);border-color:var(--line2);background:var(--panel3)}
  .chip.on{background:color-mix(in srgb,var(--gold) 15%,transparent);border-color:color-mix(in srgb,var(--gold) 55%,transparent);color:var(--gold-txt)}

  /* ---- grid + cards ---- */
  /* align-items:start, because the default stretch made every card in a row as
     tall as the tallest one in it. A ranked account fills that height with a
     chart, a peak and a winrate bar; an unranked one has none of those, so it got
     the same box with a hundred pixels of nothing between its notes and its
     footer. Ragged bottoms are honest — a hollow card is not. */
  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(335px,1fr));gap:16px}
  /* Not on .grid itself. The list is a flex column, where align-items:start stops
     meaning "don't stretch the short card to the row height" and starts meaning
     "shrink every row to the width of its own text" — which cut the list off in
     the middle of the page and left half the screen empty. */
  .grid:not(.list){align-items:start}
  /* Sixty cards, each with an SVG chart, is a lot of layout and paint for a page
     that shows six of them at a time. This lets the browser skip everything about
     a card that is nowhere near the viewport. `auto` on the intrinsic size means
     it remembers each card's real height after the first pass, so the scrollbar
     stops lying about the page length as soon as a card has been seen once.
     Only the card grid: a list row is cheap enough that the bookkeeping would
     cost more than the skipping saves. */
  .grid:not(.list):not(.wall)>.hx{content-visibility:auto;contain-intrinsic-size:auto 420px}

  /* ---- wall view ----
     Sixty accounts on one screen. No chart, no winrate, no timestamp — the
     portrait, the crest and the rank, which between them are enough to find the
     account you were thinking of. Clicking a tile hands you back to the card view
     at that account, because that is where everything you would then do lives. */
  .grid.wall{grid-template-columns:repeat(auto-fill,minmax(126px,1fr));gap:11px}
  .tile{position:relative;isolation:isolate;overflow:hidden;cursor:pointer;
    padding:14px 9px 12px;display:flex;flex-direction:column;align-items:center;gap:5px;text-align:center;
    transition:background .16s ease}
  .tile:hover{background:#ffffff08}
  .tile:focus-visible{outline-offset:-3px}
  .tile::before{content:"";position:absolute;left:0;top:10px;bottom:10px;width:3px;
    border-radius:0 3px 3px 0;background:var(--st,transparent);opacity:.85}
  .t-pic{width:52px;height:52px;border-radius:50%;position:relative;flex-shrink:0;
    background:linear-gradient(150deg,var(--panel3),var(--panel2));box-shadow:inset 0 0 0 1px var(--line);
    display:flex;align-items:center;justify-content:center;margin-bottom:3px}
  .t-pic img{width:100%;height:100%;object-fit:cover;display:block;border-radius:50%}
  .t-pic b{font-size:calc(20px*var(--ts));font-weight:600;color:var(--faint)}
  .t-crest{position:absolute;right:-7px;top:-7px;width:21px;height:21px;
    filter:drop-shadow(0 1px 2px #000000cc)}
  .t-crest .crest{width:100%;height:100%}
  .t-lv{position:absolute;left:50%;bottom:-6px;transform:translateX(-50%);
    font-style:normal;font-size:calc(10px*var(--ts));font-weight:700;line-height:calc(14px*var(--ts));padding:0 5px;
    min-width:20px;text-align:center;border-radius:var(--r-xs);font-variant-numeric:tabular-nums;
    color:var(--text);background:var(--panel);box-shadow:0 0 0 1px var(--line2)}
  .t-nm{font-size:calc(12px*var(--ts));font-weight:600;letter-spacing:-.01em;color:var(--text);max-width:100%;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:4px}
  .t-nm .rw-star .ico{width:10px;height:10px}
  .t-rk{font-size:calc(12px*var(--ts));font-weight:700;max-width:100%;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .t-lp{font-size:calc(10px*var(--ts));font-weight:600;color:var(--faint);font-variant-numeric:tabular-nums}
  .grid.wall>.hx:hover{transform:translateY(-2px)}
  @media (max-width:520px){.grid.wall{grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:9px}}

  /* ---- list view ----
     Sixty cards is thirteen screens and no two accounts ever visible together.
     Same facts, one line each, in columns you read straight down. Clicking a row
     unfolds the chart and the details under it, so nothing is hidden — the card
     grid stays for browsing, this is for working through a collection. */
  .grid.list{display:flex;flex-direction:column;gap:5px}
  /* Set in the display serif at first, on the theory that one more use would tie
     the identity together. It doesn't — the wordmark is a logo and reads as one,
     and the same face on a row of small uppercase labels inside a dense list just
     reads as the wrong font. Back to the interface face everything else uses. */
  .lgrp{display:flex;align-items:center;gap:9px;padding:15px 5px 6px}
  .lgrp:first-child{padding-top:3px}
  .lgrp-crest{width:16px;height:16px;flex-shrink:0}
  .lgrp-dot{width:8px;height:8px;border-radius:2px;transform:rotate(45deg);flex-shrink:0;opacity:.8}
  .lgrp-n{font-size:calc(11px*var(--ts));font-weight:700;letter-spacing:.14em;text-transform:uppercase}
  .lgrp-ln{flex:1;height:1px;background:linear-gradient(90deg,var(--line2),transparent)}
  .lgrp-k{font-size:calc(11px*var(--ts));font-weight:600;color:var(--faint);letter-spacing:.05em;
    font-variant-numeric:tabular-nums}
  .hx.row{padding:1px;background:#ffffff0d;border-radius:var(--r-md)}
  .hx.row:hover{background:#ffffff1c}
  .hx.row.open{background:color-mix(in srgb,var(--teal) 34%,transparent)}
  .rw{border-radius:calc(var(--r-md) - 1px);background:var(--panel);overflow:hidden}
  /* Roughly 46px of row, of which about a third was padding: a 30px picture,
     13.5px type, and six hundred pixels of nothing between the name and the rank.
     Taller, and with that gap given to the two things the list had been throwing
     away — the shape of the climb, and how the wins and losses actually sit. */
  /* The name is the one column that stretches. It used to share the slack with the
     rank (1.35fr and 1fr), so a wide screen opened two empty bands — one after the
     name and one after the rank — and the figures crowded at the right edge. Now
     the slack is all after the name, like any table, and the figures sit together
     as one block under their headings. The heading row uses the same columns. */
  .grid.list{--rw-cols:auto 40px minmax(160px,1fr) 124px 124px 60px 94px 38px 72px 62px 16px;--rw-gap:13px}
  /* Up to a point: past about 1180px, "all the slack after the name" was a hole
     between a name and its own rank (450px of nothing at 1440, a thousand at 2560).
     There every column after the picture takes a share, more for the name and the
     climb, the way a full-width table spreads; the picture stays beside the name.
     Below that width the narrower templates further down take over. */
  @media (min-width:1181px){
    .grid.list{--rw-cols:auto 40px minmax(160px,2.4fr) minmax(124px,1.4fr) minmax(124px,1fr) minmax(60px,.6fr) minmax(94px,1fr) minmax(38px,.5fr) minmax(72px,.8fr) minmax(62px,.5fr) 16px}
    /* a wider column is room around the change pill and the record bar, not a
       longer pill or a bar whose length stops meaning the same thing row to row */
    .rw-d{align-items:flex-start}
    .rw-wr{max-width:150px}
  }
  .rw-main{display:grid;align-items:center;gap:var(--rw-gap);padding:11px 14px;cursor:pointer;
    grid-template-columns:var(--rw-cols);
    transition:background .14s ease}
  .rw-head{display:grid;grid-template-columns:var(--rw-cols);gap:var(--rw-gap);align-items:end;padding:2px 15px 8px;
    font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);white-space:nowrap}
  .rw-head .h-chk{width:15px}
  .rw-head .h-r{text-align:right}.rw-head .h-c{text-align:center}
  /* copy the login straight from the row, the way a password manager's list does */
  .rw-keys{display:flex;gap:4px;justify-content:flex-end}
  .rw-key{width:28px;height:28px;padding:0;color:var(--muted)}
  .rw-key .ico{width:14px;height:14px}
  .rw-key:hover{color:var(--text)}
  .rw-main:hover{background:#ffffff07}
  .rw-main:focus-visible{outline-offset:-2px}
  .rw{position:relative}
  .rw::before{content:"";position:absolute;left:0;top:8px;bottom:8px;width:3px;z-index:1;
    border-radius:0 3px 3px 0;background:var(--st,transparent);opacity:.85}
  /* a row is a line in a list, not a tile — it must not lift off the page */
  .grid.list>.hx:hover{transform:none;box-shadow:var(--sh-1)}
  .rw-pic{width:40px;height:40px;border-radius:50%;position:relative;overflow:visible;
    background:linear-gradient(150deg,var(--panel3),var(--panel2));box-shadow:inset 0 0 0 1px var(--line);
    display:flex;align-items:center;justify-content:center}
  .rw-pic img{width:100%;height:100%;object-fit:cover;display:block;border-radius:50%}
  .rw-pic b{font-size:calc(16px*var(--ts));font-weight:600;color:var(--faint)}
  /* the tier, on the picture, the way the card puts it beside the rank */
  .rw-crest{position:absolute;right:-6px;top:-6px;width:17px;height:17px;
    filter:drop-shadow(0 1px 2px #000000cc)}
  .rw-crest .crest{width:100%;height:100%}
  /* same idea as the card: the level belongs to the picture, not to a column of
     performance numbers */
  .rw-lvb{position:absolute;left:50%;bottom:-5px;transform:translateX(-50%);
    font-style:normal;font-size:calc(10px*var(--ts));font-weight:700;line-height:calc(13px*var(--ts));padding:0 4px;
    min-width:18px;text-align:center;border-radius:var(--r-xs);font-variant-numeric:tabular-nums;
    color:var(--text);background:var(--panel);box-shadow:0 0 0 1px var(--line2)}
  .rw-id{min-width:0;display:flex;flex-direction:column;gap:1px}
  .rw-nm{font-size:calc(14px*var(--ts));font-weight:600;letter-spacing:-.012em;color:var(--text);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:flex;align-items:center;gap:6px}
  .rw-star{color:var(--star);display:inline-flex;flex-shrink:0}
  .rw-star .ico{width:11px;height:11px;fill:var(--star)}
  .rw-tag{font-size:calc(11px*var(--ts));color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* The tags ride on the Riot ID's line. On a line of their own they made every
     tagged row three lines tall: 85px a row, eight rows to a laptop screen, in the
     layout that exists to scan a long vault. The Riot ID keeps its width; the tags
     take what is left and are clipped first. */
  .rw-sub{display:flex;align-items:center;gap:8px;min-width:0}
  .rw-sub .rw-tag{flex:0 1 auto;min-width:40px}
  .rw-tags{display:flex;gap:4px;overflow:hidden;flex:0 1 auto;min-width:0}
  .rw-tags i{font-style:normal;font-size:calc(10px*var(--ts));letter-spacing:.02em;color:var(--muted);
    padding:1px 6px;border-radius:var(--r-xs);background:#ffffff0f;white-space:nowrap}
  /* the shape of the climb, which the list used to leave out entirely */
  .rw-spark{display:flex;align-items:center;height:30px}
  .spk{width:100%;height:100%;display:block}
  .rw-check{font-size:calc(11px*var(--ts));padding:4px 9px;gap:5px;opacity:0;color:var(--teal-txt);
    transition:opacity .15s ease,background .15s ease,border-color .15s ease}
  .rw-check .ico{width:11px;height:11px}
  .rw-check:focus-visible{opacity:1}
  /* same reason as the card's checkbox: a reveal on hover costs a touch screen its first tap */
  @media (hover:hover){.rw-main:hover .rw-check{opacity:1}}
  @media (hover:none){.rw-check{opacity:1}}
  .rw-rank{display:flex;flex-direction:column;gap:0;overflow:hidden;white-space:nowrap}
  .rw-rank b{font-size:calc(14px*var(--ts));font-weight:700;
    overflow:hidden;text-overflow:ellipsis}
  .rw-rank em{font-style:normal;font-size:calc(11px*var(--ts));font-weight:600;color:var(--muted);
    font-variant-numeric:tabular-nums}
  .rw-d,.rw-wr,.rw-lv{display:flex;flex-direction:column;gap:2px;font-variant-numeric:tabular-nums}
  .rw-wr b,.rw-lv b{font-size:calc(13px*var(--ts));font-weight:700;color:var(--muted);line-height:1.1}
  .rw-wr b.wr-good{color:var(--good)}
  .rw-wr em,.rw-lv em{font-style:normal;font-size:calc(10px*var(--ts));color:var(--faint);letter-spacing:.02em}
  /* Sixty rows of "47%" are sixty identical grey blocks. Sixty bars of differing
     length are a shape you can run your eye down. */
  /* drawn as a record, like the card's: wins, then the losses as the remainder */
  .rw-bar{position:relative;display:block;height:3px;border-radius:1px;overflow:hidden;
    background:color-mix(in srgb,var(--danger) 34%,var(--panel2))}
  .rw-bar i{display:block;height:100%;border-radius:1px 0 0 1px;box-shadow:1px 0 0 var(--bg)}
  .rw-d .delta{font-size:calc(11px*var(--ts));padding:2px 6px}
  .rw-rg{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.09em;color:var(--faint);text-align:center}
  /* An unranked row has nothing to say in four of its columns, and at full
     strength a wall of them drowns out the rows that do. */
  .rw-quiet .rw-pic{opacity:.62}
  .rw-quiet .rw-nm{color:var(--muted);font-weight:500}
  .rw-quiet .rw-rank b{font-size:calc(13px*var(--ts));font-weight:600}
  /* A banned account was marked by a 3px rail and a chip, which is the same
     weight the app gives "resting". It isn't the same thing: nothing about that
     rank will ever change again. It should read as finished from across the room. */
  .card.dead .c-portrait,.rw.dead .rw-pic{filter:grayscale(.9);opacity:.5}
  .card.dead .c-tint{opacity:.35}
  /* A banned account said so five times (rail, badge, strikethrough, a red border,
     its notes) while its Login was the brightest thing on the card. The badge and
     the strikethrough say it; the rail is gone, and Login is a plain button. */
  .card.dead .a-login{background-color:var(--panel2);background-image:var(--sheen);border-color:var(--line2);color:var(--muted);font-weight:600}
  .card.dead .a-login:hover{color:var(--text)}
  .card.dead .rk-crest,.rw.dead .rw-crest{filter:grayscale(.9);opacity:.55}
  .card.dead .rk .rk-t,.rw.dead .rw-rank b{text-decoration:line-through;
    text-decoration-thickness:1.5px;text-decoration-color:currentColor;opacity:.62}
  .card.dead .lpc-wrap,.rw.dead .spk{filter:grayscale(.75);opacity:.55}
  .card.dead .wrbar,.rw.dead .rw-bar{filter:grayscale(.9);opacity:.5}
  .rw-up{font-size:calc(11px*var(--ts));color:var(--faint);text-align:right;white-space:nowrap;
    overflow:hidden;text-overflow:ellipsis}
  .rw-up.stale{color:var(--warn-txt)}
  .rw-chev{width:14px;height:14px;color:var(--faint);transition:transform .24s cubic-bezier(.22,1,.36,1)}
  .hx.row.open .rw-chev{transform:rotate(180deg)}
  .rw-open{display:flex;flex-direction:column;gap:11px;padding:4px 13px 13px;
    border-top:1px solid var(--line);margin-top:1px;
    animation:sectionIn .22s cubic-bezier(.22,1,.36,1) both}
  .rw-open .lpc-wrap{margin:0}
  /* The columns that are nice-to-know go first, the identity and the rank stay.
     The sparkline outranks the region code and the timestamp: it is the only
     column that says anything a glance can't already get from the rank. */
  @media (max-width:1180px){
    .grid.list{--rw-cols:auto 40px minmax(130px,1fr) 104px 112px 58px 88px 72px 62px 16px;--rw-gap:11px}
    .rw-rg,.h-rg{display:none}
  }
  @media (max-width:980px){
    .grid.list{--rw-cols:auto 40px minmax(120px,1fr) 92px 104px 56px 62px 16px;--rw-gap:10px}
    .rw-wr,.rw-up,.h-wr,.h-up{display:none}
  }
  /* Fixed minimums here — 110px of name, 90px of rank, 54px of change — came to 36px
     more than a 390px row holds, so every row overflowed: "Diamond IV" was cut to
     "Diamond…", the LP pill was sliced and the chevron fell off the edge. The rank
     and the change get the width their longest label needs ("Grandmaster", a four-
     figure drop), fixed so they still line up from row to row; the name takes what
     is left and ellipsises. */
  @media (max-width:720px){
    .grid.list{--rw-cols:auto 36px minmax(0,1fr) 106px 58px 16px;--rw-gap:10px}
    .rw-main{min-height:52px;padding:12px 12px;align-items:center}
    .rw-spark,.rw-tags,.rw-keys,.rw-head{display:none}
    /* A phone row is two lines: the name, the rank under it, and the login's copy
       buttons on the right. It used to hide those buttons ("a phone is not where a
       client gets logged into"), but a phone is exactly where people read a
       password off while typing it into the PC, and the row showed a checkbox
       instead, which is the thing you need least. Placed by area, so whatever is
       hidden leaves no hole. The checkboxes come with Select. */
    .grid.list .rw-main{grid-template-columns:36px minmax(0,1fr) auto;grid-template-areas:"pic id keys" "pic rank keys";
      column-gap:12px;row-gap:1px;padding:10px 10px 10px 12px}
    body.selecting .grid.list .rw-main{grid-template-columns:auto 36px minmax(0,1fr) auto;
      grid-template-areas:"chk pic id keys" "chk pic rank keys"}
    .grid.list .bulkchk{grid-area:chk}
    .grid.list .rw-pic{grid-area:pic}
    .grid.list .rw-id{grid-area:id;align-self:end}
    .grid.list .rw-rank{grid-area:rank;flex-direction:row;align-items:baseline;gap:6px;align-self:start}
    .grid.list .rw-keys{grid-area:keys;display:flex;gap:6px}
    .grid.list .rw-key{width:40px;height:40px}
    .grid.list .rw-sub,.grid.list .rw-d,.grid.list .rw-chev{display:none}
    body:not(.selecting) .bulkchk{display:none}
    .tools>.t-select{display:inline-flex}
    .rw-check{opacity:1} /* thumb-friendly: always show Refresh on phones */
    .rw-pic{width:36px;height:36px}
    .acts{flex-wrap:wrap;gap:6px}
    .acts button,.a-login{min-height:40px;padding:8px 12px}
    .card{padding:14px}
    .tools{gap:8px}
    .cmd{min-height:44px}
    .dash{grid-template-columns:repeat(auto-fit,minmax(140px,1fr))}
    .stat{padding:14px 14px}
    .bulkchk{opacity:1} /* selection without needing hover */
  }
  /* On the smallest phones the rank and a name you can read do not both fit beside
     the LP change, and the rule above says which goes: the nice-to-know column. The
     change is still on the card, and in the chart when the row is opened. */
  @media (max-width:440px){
    /* a 360 phone's card has ~190px beside the crest and the LP chip, and
       "Grandmaster" at full size wants most of it */
    .card .rk-t{font-size:calc(20px*var(--ts))} /* .card: the base rule comes later in the sheet */
    .grid.list{--rw-cols:auto 36px minmax(0,1fr) 106px 16px;--rw-gap:8px}
    .rw-d{display:none}
  }
  @supports (padding:max(0px)){
    body{padding-bottom:max(12px,env(safe-area-inset-bottom))}
  }
  .card{position:relative;padding:17px;display:flex;flex-direction:column;gap:11px;
    overflow:hidden;isolation:isolate}
  /* status as a left edge rail — scannable down a column of 60 cards in a way a
     7px dot next to the name never was */
  .card::before{content:"";position:absolute;left:0;top:16px;bottom:16px;width:3px;
    border-radius:0 3px 3px 0;background:var(--st,transparent);opacity:.85}
  .ico{width:1em;height:1em;display:inline-block;vertical-align:-.15em;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;flex-shrink:0}

  /* ---- card header: portrait anchors it, name and ID stack beside it ---- */
  .c-head{display:flex;align-items:center;gap:13px;min-width:0}
  .c-portrait{position:relative;width:50px;height:50px;flex-shrink:0}
  /* Round, in a gold ring with the level on a plate beneath: the way the client
     frames a summoner icon, and the one shape here nobody mistakes for a generic
     avatar. */
  .c-initial{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;border-radius:50%;
    background:linear-gradient(150deg,var(--panel3),var(--panel2));
    box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--gold-dim) 80%,transparent),0 0 0 2px var(--bg),0 0 0 3px color-mix(in srgb,var(--gold-dim) 55%,transparent);
    font-size:calc(20px*var(--ts));font-weight:600;color:var(--faint);letter-spacing:-.02em}
  .c-avatar{position:absolute;inset:0;width:100%;height:100%;border-radius:50%;object-fit:cover;
    box-shadow:0 0 0 1px var(--gold-dim),0 0 0 2px var(--bg),0 0 0 3px color-mix(in srgb,var(--gold) 55%,transparent),0 4px 12px #00000073}
  /* The rank crest rides the portrait; it moved to the top corner to make room
     for the level, which now sits centred on the bottom edge the way op.gg puts
     it. Level was in the stats row next to Peak and MMR, which read as another
     performance figure — it isn't one. It belongs to the identity of the
     account, so it belongs on the picture. */
  /* Was a rotated square in the tier colour — a shape that says "there is a tier"
     without saying which. The crest says which. */
  .crest{display:block;flex-shrink:0;overflow:visible}
  .c-lvbadge{position:absolute;left:50%;bottom:-7px;transform:translateX(-50%);
    min-width:24px;padding:1px 6px;border-radius:var(--r-xs);text-align:center;
    font-size:calc(11px*var(--ts));font-weight:700;line-height:calc(16px*var(--ts));letter-spacing:.01em;
    font-variant-numeric:tabular-nums;color:var(--text);
    background:var(--panel);box-shadow:0 0 0 1px var(--line2),0 2px 8px #00000080}
  .c-id{min-width:0;flex:1}
  .c-name{font-weight:600;font-size:calc(18px*var(--ts));letter-spacing:-.022em;line-height:1.25;display:flex;align-items:center;flex-wrap:wrap;gap:3px 8px;min-width:0}
  /* text-overflow only works on a box that holds the text itself. The wall tile and
     the list row had the name as a bare text node in a flex row, so it was sheared
     off mid-letter, no ellipsis, with the flag after it pushed out of sight — the
     card never did, because its name was already a span of its own. */
  .c-name>span:first-child,.t-nm>.nm-t,.rw-nm>.nm-t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}
  /* The chip may not take the room the account name needs: "Resting / unused"
     was holding 114 px of a 234 px row and leaving the name 58 — four letters
     and an ellipsis. Short wording here, the full phrase in the title and
     everywhere else. It used to give way by shrinking too, and on a four-across
     grid both halves lost: "Fresh leveli…" beside "RES…", a name cut short and a
     status nobody could read. Now the row wraps instead: the name keeps the first
     line, and a chip that does not fit beside it drops under it, whole. */
  .stchip{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.07em;text-transform:uppercase;padding:3px 7px;border-radius:var(--r-xs);
    flex:0 0 auto;white-space:nowrap}
  .c-chrome{display:flex;align-items:center;gap:9px;flex-shrink:0;align-self:flex-start}
  /* selection is chrome, not content — it stays out of the way until wanted */
  .bulkchk{width:15px;height:15px;flex-shrink:0;accent-color:var(--teal);cursor:pointer;margin:0;
    opacity:0;transition:opacity .15s ease}
  .bulkchk:checked,.bulkchk:focus-visible{opacity:1}
  /* Revealed by hovering the card, where there is a pointer to hover with. On a
     touch screen the tap is the hover, and Safari on iPad and iPhone treats a tap
     that makes something appear as "show me", not as a click: Login, Refresh,
     anything on the card took a second tap, the first one only lifting the card.
     There the box is simply shown, faintly, and a tap is a tap. */
  @media (hover:hover){.card:hover .bulkchk,.rw:hover .bulkchk{opacity:1}}
  @media (hover:none){.bulkchk{opacity:.5}}

  /* bare icon buttons — must beat the generic button rules */
  .rid,.star,.eye-btn,.panel-close,.c-ref{background:none;border:1px solid transparent;box-shadow:none;padding:0}
  /* the card's Refresh: an icon on the timestamp's line */
  .c-ref{width:26px;height:26px;margin:-5px 0 -5px -5px;display:inline-flex;align-items:center;justify-content:center;
    color:var(--teal-txt);border-radius:var(--r-xs)}
  .c-ref .ico{width:13px;height:13px}
  .c-ref:hover:not(:disabled){background:#ffffff0d;border-color:var(--line)}
  .c-ref:disabled{opacity:.7;cursor:default}
  .rid:hover,.star:hover,.eye-btn:hover{background:none;border-color:transparent}
  .star{font-size:calc(18px*var(--ts));color:var(--line2);line-height:1;display:inline-flex;transition:color .15s ease,transform .15s ease}
  .star:hover{color:var(--star);transform:scale(1.12)}
  .star svg{fill:none;stroke:currentColor;stroke-width:1.7}
  .star.on{color:var(--star)}
  .star.on svg{fill:currentColor}
  /* Same face as everything else. Monospace was defensible in theory — it is an
     identifier — but sitting one line under the name it just read as a font
     glitch, so the tag is separated by colour and weight instead. */
  /* text-overflow on a <button> does nothing: the button's anonymous inner box
     doesn't shrink, so the label just got clipped mid-word with no ellipsis. The
     truncation has to happen on a real element inside it. */
  .rid{font-size:calc(12px*var(--ts));color:var(--muted);cursor:pointer;text-align:left;justify-content:flex-start;margin-top:2px;
    letter-spacing:-.005em;transition:color .15s ease;width:100%;max-width:100%;
    display:flex;min-width:0;overflow:hidden}
  .rid>span{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .rid b{color:var(--faint);font-weight:400;transition:color .15s ease}
  /* the tag is part of the same name — hovering must light all of it, not just
     the half before the # */
  .rid:hover,.rid:hover b{color:var(--gold-txt)}
  .rid:hover b{opacity:.72}
  .eye-btn{position:absolute;right:4px;top:50%;transform:translateY(-50%);padding:6px;color:var(--muted)}
  .eye-btn:hover{color:var(--text)}
  .panel-close{position:absolute;top:13px;right:13px;width:29px;height:29px;border-radius:var(--r-sm);
    color:var(--muted);z-index:2;font-size:calc(13px*var(--ts))}
  .panel-close:hover{color:var(--danger);background:color-mix(in srgb,var(--danger) 13%,transparent);border-color:transparent}

  /* A label, not an action, so it is not in the action colour: tinted with the
     secondary accent, every card carried a second chip of Login's colour right
     beside the star, and the one button meant to stand out had company. */
  .regiontag{font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.1em;padding:4px 9px;border-radius:var(--r-xs);flex-shrink:0;
    background:#ffffff0a;color:var(--muted);border:1px solid var(--line)}
  /* the rank is why the card exists, so it gets the card's largest type */
  /* The tier as an emblem rather than only as a coloured word. It sits in the
     rank line itself rather than watermarked behind it: faint behind the type it
     landed under the delta chips, where it was both invisible and in the way.
     A second, much larger copy bleeds off the right edge at low opacity, which is
     what gives an Iron card and a Challenger card different weight from across
     the room — that one is texture, and never has to be read. */
  .c-rank{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-top:3px}
  /* The tier as a wash over the card rather than as a picture on it.
     A large crest bleeding off the right edge was the first attempt: it read as a
     shape half-hidden behind the delta chips — the eye keeps trying to complete it
     and there is nothing to complete. A tint has no silhouette to be cut off, so
     it can sit at the strength needed to be felt from across the room without ever
     asking to be looked at. Anchored top-left, away from every chip and number.
     isolation:isolate is what lets z-index:-1 paint above the card's own
     background while still staying under every child. */
  /* The champion the account plays most, faded into the top of the card under the
     tier tint. Low and masked off well before the rank and the buttons, so it is
     felt rather than read; nothing on the card depends on it. 2.3.0 tried 18%,
     and on a real iPad it nearly vanished, so it is back at 28%. */
  .c-art{position:absolute;left:0;right:0;top:0;width:100%;height:230px;z-index:-2;pointer-events:none;
    object-fit:cover;object-position:center 18%;opacity:var(--art-op,.28);
    -webkit-mask-image:linear-gradient(180deg,#000,#0008 45%,transparent 92%);mask-image:linear-gradient(180deg,#000,#0008 45%,transparent 92%)}
  /* --art-op: Card art… → Strength. Set on the root for every card, or on one
     card's own picture; a banned card keeps its art at about a third of that. */
  .card.dead .c-art{filter:grayscale(1);opacity:calc(var(--art-op,.28) * .36)}
  /* A hint of the tier in the corner the rank sits in. At 15% over the art it
     tinted the whole top of the card, so a row of cards came out yellow, teal and
     red under their splashes; the rank's own colour and crest already say it. */
  .c-tint{position:absolute;inset:0;z-index:-1;pointer-events:none;
    background:radial-gradient(110% 64% at 0% 0%,
      color-mix(in srgb,var(--tier) 7%,transparent),transparent 58%)}
  .c-deltas{display:flex;flex-direction:column;align-items:flex-end;gap:5px;flex-shrink:0}
  /* the note's first two lines on the card face; all of it in Details or the editor */
  .c-notes.capped .ni-row{flex-wrap:nowrap}
  .c-notes.capped .ni-row>.ni-f{min-width:0;flex-shrink:1}
  .c-notes.capped .ni-row>.ni-f.more{flex-shrink:0}
  .c-notes.capped .nt{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .lvt{border:1px solid var(--line);border-radius:var(--r-sm);padding:11px 12px 10px;background:linear-gradient(180deg,#ffffff06,transparent)}
  .lvt-h{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:10px}
  .lvt-h .k{color:var(--faint)}
  .lvt-r{font-size:calc(12px*var(--ts));color:var(--muted);font-variant-numeric:tabular-nums}
  .lvt-track{display:grid;grid-template-columns:repeat(30,minmax(0,1fr));gap:2px;height:22px;align-items:end}
  .lvt-track i{height:70%;border-radius:1px;background:#ffffff14}
  .lvt-track i.m{height:100%}
  .lvt-track i.on{background:color-mix(in srgb,var(--teal) 70%,transparent)}
  .lvt-track i.now{background:var(--teal);height:100%;box-shadow:0 0 0 1px var(--panel),0 0 0 2px color-mix(in srgb,var(--teal) 60%,transparent)}
  /* on the track's own 30 columns, so 10 and 20 sit under their taller bars */
  .lvt-ax{display:grid;grid-template-columns:repeat(30,minmax(0,1fr));gap:2px;font-size:calc(10px*var(--ts));color:var(--faint);margin-top:6px;letter-spacing:.02em;white-space:nowrap}
  .lvt-pace{font-size:calc(11px*var(--ts));color:var(--muted);margin-top:7px}
  .d-cap{font-size:calc(10px*var(--ts));color:var(--faint);letter-spacing:.02em;white-space:nowrap;margin-top:-2px}
  /* Rank is the headline; LP is a labelled companion under it — not a smaller
     word jammed onto the same baseline with a leftover margin. */
  .rk{display:flex;align-items:center;gap:11px;min-width:0;white-space:nowrap;
    overflow:hidden}
  .rk-main{display:flex;flex-direction:column;gap:3px;min-width:0;line-height:1}
  .rk-t{font-size:calc(22px*var(--ts));letter-spacing:-.02em;line-height:1.1;
    overflow:hidden;text-overflow:ellipsis}
  /* No coloured glow. A 9px halo in the tier's own full-saturation colour around
     a 27px shape stops reading as a lit emblem and starts reading as a lit square
     box — the glow is larger than the thing it is meant to be lighting. The crest
     carries its own highlight in the gradient; all it needs from here is enough
     shadow to sit off the panel. */
  .rk-crest{width:28px;height:28px;flex-shrink:0;
    filter:drop-shadow(0 1px 2px #000000a6)}
  .rk-lp{font-size:calc(13px*var(--ts));font-weight:600;letter-spacing:.04em;margin:0;
    font-variant-numeric:tabular-nums;color:var(--muted);opacity:1}
  .c-unchecked{font-size:calc(18px*var(--ts));color:var(--faint);letter-spacing:-.02em}
  /* on an account with no rank yet, the level is the headline — it was set in
     10 px caps underneath a 23 px "Unranked" that answered nothing */
  .c-lvl{font-size:calc(16px*var(--ts));color:var(--faint);letter-spacing:.02em;font-weight:500;
    display:flex;align-items:baseline;gap:8px}
  .c-lvl .rk-lp{font-size:calc(24px*var(--ts));color:var(--text);letter-spacing:-.03em}
  .wl{font-size:calc(13px*var(--ts));color:var(--muted);font-variant-numeric:tabular-nums;margin-top:4px}
  .wl b{color:var(--text);font-weight:500}
  /* winrate carries the verdict, so it gets the only colour on the line */
  .wrv{font-weight:600;color:var(--text)}
  .wrv.wr-good{color:var(--good)}

  /* labelled micro-stats instead of one muted run-on sentence */
  .c-stats{display:flex;flex-wrap:wrap;gap:7px 20px}
  /* Four is the usual count — Peak, Climb, MMR, Recent avg — and on a card four
     across does not fit, so it wrapped three and one: a lone "Recent avg" on a row
     of its own, under nothing. An even count reads as pairs instead, two by two in
     two aligned columns. An odd one keeps flowing at its own widths, where a long
     peak ("Platinum II · 61 LP") has the room it needs beside two short ones. */
  .c-stats.n2,.c-stats.n4,.c-stats.n6{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px 20px}
  /* Settings → On each card: a part left under Details is hidden on a closed card */
  body.hide-chart .card:not(.info-open) .lpc,body.hide-stats .card:not(.info-open) .c-stats,
  body.hide-wl .card:not(.info-open) .wrbar,body.hide-goal .card:not(.info-open) .goal,
  body.hide-goal .card:not(.info-open) .lvt,
  body.hide-tags .card:not(.info-open) .ctags,body.hide-notes .card:not(.info-open) .c-notes:not(.editing){display:none}
  .c-stats>div{display:flex;flex-direction:column;gap:2px;min-width:0}
  .c-stats .k{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--faint)}
  .c-stats .v{font-size:calc(13px*var(--ts));font-weight:500;color:var(--text);letter-spacing:-.01em;font-variant-numeric:tabular-nums}
  /* This is the one thing on the card the user wrote themselves, so it reads as
     content on its own small surface rather than another grey caption line. */
  .c-notes{font-size:calc(13px*var(--ts));line-height:1.6;color:var(--text);
    padding:9px 12px;border-radius:var(--r-sm);background:#ffffff0a;border:1px solid #ffffff1a;
    transition:background .16s ease,border-color .16s ease}
  /* the note is the one field you actually keep rewriting, so it edits where it
     lives — no round trip through the Edit form */
  .c-notes[data-act]{cursor:text}
  .c-notes[data-act]:hover{background:#ffffff0f;border-color:#ffffff21}
  .c-notes .k{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--faint);
    display:flex;align-items:center;gap:5px;margin-bottom:4px}
  .c-notes .k .ico{width:11px;height:11px;opacity:0;transition:opacity .16s ease}
  @media (hover:hover){.c-notes[data-act]:hover .k .ico{opacity:.85}}
  @media (hover:none){.c-notes[data-act] .k .ico{opacity:.6}}
  .c-notes .none{color:var(--faint)}
  .c-notes textarea{width:100%;font-size:calc(13px*var(--ts));line-height:1.6;min-height:62px;display:block}
  .c-notes.editing{border-color:color-mix(in srgb,var(--teal) 34%,transparent);background:#ffffff0d}
  /* An empty note is an invitation, not a panel. It still had a dashed box around
     it, which on a vault where almost nothing is annotated makes it the most
     repeated element on the page — fifty-one outlined rectangles, all empty.
     One quiet line now; the box arrives with the note, or as soon as you click. */
  .c-notes.blank{display:flex;align-items:center;gap:6px;padding:3px 5px;
    background:none;border:0;color:var(--faint);font-size:calc(11px*var(--ts));opacity:.9}
  .c-notes.blank:hover{background:#ffffff0d;border:0;color:var(--muted);opacity:1}
  .c-notes.blank .ico{width:11px;height:11px;opacity:.8}
  /* ---- note fields on the card ----
     Honor, skins, Blue Essence and the rest, each in the same place on every
     card, so sixty accounts can be read down a column instead of one paragraph
     at a time. Numbers are facts on plates; skins are the gold ones, being what
     the account is worth to you; a penalty is the one red line. */
  .c-notes .nt{white-space:pre-line}
  .c-notes .ni{display:flex;flex-direction:column;gap:7px;margin:2px 0 1px;cursor:pointer}
  .c-notes .ni+.nt{margin-top:8px;padding-top:8px;border-top:1px solid #ffffff17}
  .c-notes.editing .ni{cursor:default;margin-bottom:9px}
  .ni-row{display:flex;flex-wrap:wrap;gap:5px}
  /* Read on a tablet across the room, these were grey words on a plate barely
     lighter than the card: the numbers stood out and everything around them sank.
     The words are a light parchment now, a step under the figures rather than
     three, and the plate has an edge you can see. */
  .ni-f{display:inline-flex;align-items:center;gap:6px;padding:4px 9px;border-radius:var(--r-xs);
    font-size:calc(12px*var(--ts));line-height:1.35;color:color-mix(in srgb,var(--text) 74%,var(--muted));
    background:#ffffff10;border:1px solid #ffffff21;box-shadow:inset 0 1px 0 #ffffff0a;
    font-variant-numeric:tabular-nums;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
  .ni-f b{color:#fff;font-weight:600}
  .ni-f .ico{width:12px;height:12px;flex-shrink:0}
  .ni-f.ok{color:var(--good);border-color:color-mix(in srgb,var(--good) 40%,transparent);background:color-mix(in srgb,var(--good) 10%,transparent)}
  .ni-f.warn{color:var(--warn-txt);border-color:color-mix(in srgb,var(--warn) 40%,transparent);background:color-mix(in srgb,var(--warn) 11%,transparent)}
  .ni-pips{display:inline-flex;gap:3px}
  .ni-pips i{width:7px;height:7px;transform:rotate(45deg);border-radius:1px;background:#ffffff38}
  .ni-pips i.on{background:var(--gold-txt)}
  /* Skins wore the accent, a fourth chip style beside the plain facts, the green
     and amber states and the tags, and in an accent colour they read as pressed
     filters. They are facts like the others; their own row says what they are. */
  .ni-skins span.more{color:var(--muted);background:none;border-style:dashed}
  .ni-pen{display:flex;align-items:flex-start;gap:7px;padding:6px 9px;border-radius:var(--r-xs);font-size:calc(12px*var(--ts));line-height:1.45;
    color:#ffc4c4;background:#ff6b6b12;border:1px solid #ff6b6b2e}
  .ni-pen .ico{width:13px;height:13px;flex-shrink:0;margin-top:2px;color:var(--danger)}
  .ni-kv{display:grid;grid-template-columns:auto minmax(0,1fr);gap:3px 10px;font-size:calc(12px*var(--ts));line-height:1.45}
  .ni-kv .k2{color:var(--muted)}
  .ni-kv .v2{color:var(--text);overflow-wrap:anywhere}
  /* the second way in from an empty note, for when what you want to write down
     is a field; shown on hover where there is hover, always where there isn't */
  .c-notes.blank{gap:4px}
  .c-notes.blank button{all:unset;display:inline-flex;align-items:center;gap:6px;cursor:pointer;padding:2px 4px;border-radius:var(--r-xs)}
  .c-notes.blank button:focus-visible{outline:2px solid var(--teal-txt);outline-offset:1px}
  .c-notes.blank .nf-link{margin-left:auto;color:var(--faint)}
  .c-notes.blank .nf-link:hover{color:var(--gold-txt)}
  @media (hover:hover){
    .c-notes.blank .nf-link{opacity:0;transition:opacity .16s ease}
    :is(.card,.rw):hover .c-notes.blank .nf-link,.c-notes.blank .nf-link:focus-visible{opacity:1}
  }
  /* ---- note fields in the account form ---- */
  /* .fld is a column further down the sheet; this one row of it is a header */
  .fld.nf-head{flex-direction:row;align-items:center;gap:10px;padding-top:18px;border-top:1px solid var(--line)}
  .nf-head .nf-t{font-size:calc(14px*var(--ts));font-weight:600;color:var(--text)}
  .nf-head .hint{flex:1;min-width:0}
  .nf-head button{padding:6px 11px;font-size:calc(12px*var(--ts))}
  .nf-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(150px,100%),1fr));gap:14px 16px}
  /* grid items keep their content's width as a floor unless told otherwise, and
     a row of input, kind and button is wider than a phone's single column */
  .formgrid>*,.nf-grid>*{min-width:0}
  .nf-grid .fld.wide{grid-column:1/-1}
  .nf-honor{width:100%}
  .nf-honor button{flex:1;width:auto;min-width:0;height:36px;font-size:calc(13px*var(--ts));font-weight:600;color:var(--muted)}
  .nf-cfg{border:1px solid var(--line);border-radius:var(--r-md);background:#00000026;padding:14px 16px;display:flex;flex-direction:column;gap:12px}
  .nf-cfg .grp-h{color:var(--faint)}
  .nf-on{display:flex;flex-wrap:wrap;gap:8px 18px}
  .nf-on label{display:inline-flex;align-items:center;gap:7px;font-size:calc(13px*var(--ts));color:var(--text);cursor:pointer}
  .nf-own{display:flex;flex-direction:column;gap:8px}
  .nf-own .row{display:flex;gap:8px;align-items:stretch}
  .nf-own .row input{flex:1;min-width:0}
  .nf-own .row select{width:128px;flex-shrink:0}
  .nf-own .row button{flex-shrink:0;min-width:58px;padding:7px 10px}
  .nf-own .none{font-size:calc(12px*var(--ts));color:var(--faint)}
  @media (max-width:560px){.nf-cfg{padding:12px}.nf-own .row{gap:6px}.nf-own .row select{width:94px}.nf-own .row button{min-width:42px}}
  /* ---- the details drawer ----
     Details was one button among six in the action row — a poor home for the one
     control that changes the card's height, and easy to miss among five things
     that don't. A bar across the card's bottom edge says "there is more under
     here" before the label is read, and it stays pinned there when open, so the
     panel unfolds above its own handle instead of pushing it down the page. */
  /* No rim, no fill, no rule above it: a bordered bar reads as a control bolted to
     the bottom of the card, and this is meant to be the bottom of the card. What's
     left is a line running the full width with a chevron sitting in it — the shape
     alone says "pull". It only picks up a surface under the pointer. */
  .c-drawer{margin:0 -17px -17px;padding:11px 14px 12px;gap:11px;
    border:0;border-radius:0 0 calc(var(--r-lg) - 2px) calc(var(--r-lg) - 2px);
    background:none;box-shadow:none;color:var(--faint);
    font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.16em;text-transform:uppercase;
    transition:background .18s ease,color .18s ease}
  .c-drawer:hover{color:var(--teal-txt);border:0;
    background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--teal) 11%,transparent))}
  .c-drawer.on{color:var(--teal-txt)}
  .c-drawer:active{transform:none}
  .c-dt{white-space:nowrap}
  /* the rules either side turn the chevron into one long arrow spanning the card
     rather than a small glyph adrift in the middle of a bar */
  .c-dl{flex:1;height:1px;min-width:10px;background:linear-gradient(90deg,transparent,var(--line2))}
  .c-dl:last-child{background:linear-gradient(90deg,var(--line2),transparent)}
  .c-drawer:hover .c-dl{background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--teal) 60%,transparent))}
  .c-drawer:hover .c-dl:last-child{background:linear-gradient(90deg,color-mix(in srgb,var(--teal) 60%,transparent),transparent)}
  .c-drawer .ico{width:22px;height:10px;stroke-width:2.2;flex-shrink:0;
    transition:transform .28s cubic-bezier(.22,1,.36,1)}
  .c-drawer:hover .ico{transform:translateY(2px)}
  .c-drawer.on .ico,.c-drawer.on:hover .ico{transform:rotate(180deg)}

  /* ---- info panel: past seasons, flex, champions ----
     Unrolling every season and every champion at once buried the one number you
     came for under thirty others. Each group is a headline that already answers
     most of the question — the peak of those seasons, the champion actually
     played — with the rows one click behind it. */
  .login.info{gap:0;padding:0;overflow:hidden}
  .sec{border-top:1px solid #ffffff17}
  .sec:first-child{border-top:0}
  .sec-h{width:100%;display:flex;align-items:center;gap:11px;padding:10px 13px;
    background:none;border:0;border-radius:0;box-shadow:none;white-space:normal}
  .sec-h:hover{background:#ffffff09;border:0}
  .sec-n{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--muted);
    display:inline-flex;align-items:center;gap:7px;white-space:nowrap;flex-shrink:0}
  .sec-n i{font-style:normal;font-size:calc(10px*var(--ts));letter-spacing:.03em;color:var(--faint);
    padding:1px 6px;border-radius:var(--r-xs);background:#ffffff0f}
  .sec-p{margin-left:auto;font-size:calc(12px*var(--ts));font-weight:600;letter-spacing:-.01em;color:var(--muted);
    min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:right}
  .sec-p .c-nm{display:inline}
  .sec-h .ico{width:13px;height:13px;color:var(--faint);flex-shrink:0;
    transition:transform .24s cubic-bezier(.22,1,.36,1)}
  .sec.on .sec-h .ico{transform:rotate(180deg)}
  .sec.on .sec-n{color:var(--text)}
  .sec.on .sec-p{opacity:.5}
  .sec-b{padding:1px 13px 11px;transform-origin:top center;
    animation:sectionIn .22s cubic-bezier(.22,1,.36,1) both}
  /* one fact, one line — folding it would cost a click to reveal nothing extra */
  .sec.flat{display:flex;align-items:center;gap:11px;padding:11px 13px}
  .chp-h{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.1em;text-transform:uppercase;
    color:var(--faint);padding-bottom:5px;border-bottom:1px solid #ffffff12;margin-bottom:2px}
  .chp-h span:not(:first-child){text-align:right}
  .chp-h+.chp{border-top:0}
  .sea{display:flex;align-items:baseline;justify-content:space-between;gap:10px;padding:3px 0;font-size:calc(12px*var(--ts))}
  .sea+.sea{border-top:1px solid #ffffff12}
  .s-yr{color:var(--faint);font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.09em;text-transform:uppercase}
  .s-rk{font-weight:600;font-size:calc(13px*var(--ts));letter-spacing:-.01em}
  /* a fixed grid, so the numbers line up into columns you can scan down */
  .chp{display:grid;grid-template-columns:1fr auto auto auto;gap:10px;align-items:baseline;
    padding:3px 0;font-size:calc(12px*var(--ts));font-variant-numeric:tabular-nums}
  .chp+.chp{border-top:1px solid #ffffff12}
  .c-nm{color:var(--text);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .c-wr{font-weight:600;color:var(--muted);text-align:right}
  .c-wr.wr-good{color:var(--good)}
  .c-gm,.c-kd{color:var(--faint);font-size:calc(11px*var(--ts));text-align:right}
  /* recent form: a pip per game, newest first, the way a match history reads */
  .fm-pips{display:inline-flex;gap:3px;margin-left:7px;vertical-align:-1px}
  .fm-pip{width:6px;height:11px;border-radius:2px;background:var(--danger)}
  .fm-pip.w{background:var(--good)}
  .fm-res{display:inline-block;width:1.3em;font-weight:700;font-size:calc(11px*var(--ts));color:var(--danger)}
  .fm-res.w{color:var(--good)}
  .fm-retry{margin-left:8px;padding:2px 8px;font-size:calc(11px*var(--ts))}
  /* Settings → the worker guide */
  .guide{margin-top:8px;border:1px solid var(--line);border-radius:var(--r-md);background:#ffffff05;font-size:calc(12.5px*var(--ts));color:var(--muted);line-height:1.55}
  .guide summary{cursor:pointer;padding:10px 13px;font-weight:600;color:var(--text);list-style-position:inside}
  .adv-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:14px 16px;padding:4px 13px 13px}
  .adv-grid>.fld:first-child,.adv-grid>.hint{grid-column:1/-1}
  @media (max-width:560px){.adv-grid{grid-template-columns:1fr}}
  .guide[open] summary{border-bottom:1px solid var(--line)}
  .guide ol{margin:0;padding:10px 14px 4px 32px}
  .guide li{margin-bottom:9px}
  .guide li::marker{color:var(--gold-txt);font-weight:700}
  .guide a{color:var(--teal-txt)}
  .guide .guide-btn{display:inline-block;padding:3px 10px;border-radius:var(--r-sm);font-weight:600;text-decoration:none;
    color:var(--text);border:1px solid color-mix(in srgb,var(--teal) 55%,transparent);background:color-mix(in srgb,var(--teal) 14%,transparent)}
  .guide .guide-btn:hover{background:color-mix(in srgb,var(--teal) 24%,transparent)}
  .guide-alt{padding:0 14px 11px;font-size:calc(12px*var(--ts));color:var(--faint)}
  .lpc-cut{position:absolute;left:50%;transform:translateX(-50%);font-size:calc(9px*var(--ts));font-weight:700;letter-spacing:.12em;
    pointer-events:none;line-height:1;opacity:.9}
  /* Wins against losses, the way op.gg draws a record — not a fill on an empty
     track. That was exactly the goal rail's picture two lines below, so a 42% win
     rate and a goal 42% of the way there looked the same. The losses are the red
     remainder. There was a notch at 50% too, to mark even, but it cut through
     whichever side it fell in, so a 57% record drew as two wins segments and a
     losses one, a split nobody could read. The win rate beside the bar, green
     from 50%, already says which side of even it is. */
  .wrbar{position:relative;height:4px;border-radius:1px;overflow:hidden;
    background:color-mix(in srgb,var(--danger) 34%,var(--panel2))}
  .wrbar i{display:block;height:100%;border-radius:1px 0 0 1px;box-shadow:1px 0 0 var(--bg)}
  .delta{font-size:calc(11px*var(--ts));padding:2px 8px;border-radius:var(--r-xs);font-weight:600;display:inline-block;white-space:nowrap}
  .d-up{color:var(--good);background:color-mix(in srgb,var(--good) 15%,transparent)}
  .d-down{color:var(--danger);background:color-mix(in srgb,var(--danger) 15%,transparent)}
  /* the warning colour, not an amber of its own: a hard-coded hex sat a shade off --warn,
     so "Decay in ~7d" and "No 2-step" were two different yellows on one card */
  .d-warn{color:var(--warn-txt);background:color-mix(in srgb,var(--warn) 15%,transparent)}
  .wl .ready{color:var(--teal-txt);font-weight:600}
  .delta .ico{width:1em;height:1em;vertical-align:-.15em}
  /* ---- LP chart ----
     The old sparkline was a bare line with no scale: it told you the number went
     up or down but never how far, or across which ranks. This draws the actual
     ladder — a band per tier in that tier's colour, so a climb out of Emerald is
     visible as leaving the green — and every check is a hoverable point.
     Height is fixed and matches the SVG viewBox height, so viewBox units and
     pixels are the same thing and the HTML overlay can be positioned in px. */
  .lpc{position:relative;margin-top:2px;border-radius:var(--r-sm);overflow:visible;
    border:1px solid var(--line);background:#00000038}
  /* the plot's height matches the SVG viewBox height, so viewBox units and pixels
     are the same thing and the HTML overlay can be positioned in px */
  .lpc-plot{position:relative;height:88px}
  .lpc-svg{display:block;width:100%;height:100%;border-radius:var(--r-sm) var(--r-sm) 0 0}
  /* X is real time now, so the dates below are worth printing */
  .lpc-axis{position:relative;height:16px;border-top:1px solid var(--line)}
  .lpc-tick{position:absolute;top:3px;transform:translateX(-50%);white-space:nowrap;
    font-size:calc(9px*var(--ts));font-weight:500;letter-spacing:.04em;color:var(--faint);pointer-events:none}
  .lpc-tick.l{transform:none;left:5px!important}
  .lpc-tick.r{transform:none;left:auto!important;right:5px}
  .away-band{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:18px;
    padding:12px 15px;border-radius:var(--r-md);font-size:calc(13px*var(--ts));
    border:1px solid color-mix(in srgb,var(--teal) 34%,transparent);
    background:color-mix(in srgb,var(--teal) 8%,var(--panel));box-shadow:var(--sh-1);
    animation:barIn .3s cubic-bezier(.22,1,.36,1) both}
  .away-band.hidden{display:none}
  .away-k{font-size:calc(10px*var(--ts));font-weight:700;letter-spacing:.13em;text-transform:uppercase;color:var(--teal-txt)}
  .away-band b{font-weight:600;color:var(--text)}
  .away-band .up{color:var(--good);font-weight:600}
  .away-band .down{color:var(--danger);font-weight:600}
  .away-band .sp{margin-left:auto;display:flex;gap:7px}
  .away-band button{font-size:calc(12px*var(--ts));padding:5px 11px}
  @media (prefers-reduced-motion:reduce){.away-band{animation:none}}

  /* ---- quick find ---- */
  .pal{max-width:600px}
  .modal .pal{margin-top:clamp(0px,9vh,110px);margin-bottom:auto}
  .pal-h{display:flex;align-items:center;gap:11px;padding:15px 18px;border-bottom:1px solid var(--line);flex-shrink:0}
  .pal-h .ico{width:17px;height:17px;color:var(--faint);flex-shrink:0}
  .pal-h input{border:0;background:none;box-shadow:none;padding:0;font-size:calc(16px*var(--ts));flex:1;min-width:0}
  .pal-h input:focus{border:0;box-shadow:none;outline:none}
  .pal-list{flex:1;min-height:0;overflow:auto;padding:7px;display:flex;flex-direction:column;gap:1px;
    max-height:min(52vh,420px)}
  .pal-i{display:flex;align-items:center;gap:11px;padding:8px 10px;border-radius:var(--r-sm);
    cursor:pointer;text-align:left;background:none;border:0;box-shadow:none;width:100%}
  .pal-i:hover{background:#ffffff0a;border:0}
  /* Selection is driven by the keyboard, so it has to be visible without a
     pointer anywhere near it — a hover tint is not enough on its own. */
  .pal-i.sel{background:color-mix(in srgb,var(--teal) 13%,transparent);
    box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--teal) 32%,transparent)}
  .pal-pic{width:28px;height:28px;border-radius:50%;flex-shrink:0;position:relative;overflow:visible;
    background:linear-gradient(150deg,var(--panel3),var(--panel2));box-shadow:inset 0 0 0 1px var(--line);
    display:flex;align-items:center;justify-content:center;font-size:calc(12px*var(--ts));font-weight:600;color:var(--faint)}
  .pal-pic img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}
  .pal-pic .crest{position:absolute;right:-5px;top:-5px;width:14px;height:14px}
  .pal-ico{width:28px;height:28px;border-radius:var(--r-sm);flex-shrink:0;display:flex;align-items:center;
    justify-content:center;background:#ffffff0d;color:var(--muted)}
  .pal-ico .ico{width:15px;height:15px}
  .pal-t{min-width:0;flex:1;display:flex;flex-direction:column;gap:1px}
  .pal-n{font-size:calc(14px*var(--ts));font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .pal-s{font-size:calc(11px*var(--ts));color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .pal-r{font-size:calc(12px*var(--ts));font-weight:700;flex-shrink:0;letter-spacing:-.01em}
  .pal-g{font-size:calc(10px*var(--ts));font-weight:700;letter-spacing:.13em;text-transform:uppercase;color:var(--faint);
    padding:9px 10px 4px}
  .pal-empty{padding:26px 14px;text-align:center;color:var(--faint);font-size:calc(13px*var(--ts))}
  .pal-f{display:flex;gap:16px;padding:9px 16px;border-top:1px solid var(--line);flex-shrink:0;
    font-size:calc(11px*var(--ts));color:var(--faint);background:color-mix(in srgb,var(--bg) 72%,transparent)}
  .pal-f kbd{font-family:inherit;font-size:calc(10px*var(--ts));padding:1px 5px;border-radius:var(--r-xs);margin-right:3px;
    background:#ffffff12;box-shadow:inset 0 0 0 1px var(--line2)}
  .help{display:grid;grid-template-columns:118px minmax(0,1fr);gap:0 22px;margin:0;padding:8px 24px 12px;
    font-size:calc(13px*var(--ts));line-height:1.7;color:var(--muted)}
  .help dt,.help dd{padding:13px 0;border-top:1px solid var(--line)}
  .help dt:first-of-type,.help dd:first-of-type{border-top:0}
  .help dt{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-txt);line-height:2.2}
  .help dd{margin:0}
  .help dd.terms::first-letter{text-transform:uppercase}
  .help b{color:var(--text);font-weight:600}
  .help code{font-size:calc(12px*var(--ts));padding:1px 6px;white-space:nowrap}
  .help kbd{display:inline-block;min-width:20px;padding:0 6px;margin:0 1px;border-radius:var(--r-xs);text-align:center;
    font:600 calc(11px*var(--ts))/calc(18px*var(--ts)) Inter,system-ui,sans-serif;color:var(--text);background:var(--panel3);
    box-shadow:inset 0 0 0 1px var(--line2),inset 0 -2px 0 var(--line2)}
  @media (max-width:560px){.help{grid-template-columns:1fr;padding:4px 18px 10px}
    .help dt{padding:14px 0 2px}.help dd{border-top:0;padding-top:0}}
  .to-top{position:fixed;right:clamp(14px,3vw,28px);bottom:clamp(14px,3vw,28px);z-index:55;
    width:40px;height:40px;padding:0;border-radius:50%;
    background:color-mix(in srgb,var(--bg) 88%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
    border:1px solid var(--line2);box-shadow:var(--sh-2);color:var(--muted);
    transition:opacity .2s ease,transform .2s cubic-bezier(.22,1,.36,1),color .16s ease,border-color .16s ease}
  .to-top .ico{width:17px;height:17px;stroke-width:2.4}
  .to-top:hover{color:var(--text);border-color:var(--line2);transform:translateY(-2px)}
  .to-top.hidden{display:block;opacity:0;pointer-events:none;transform:translateY(8px) scale(.9)}
  /* Top-right of the plot — which is also where a high division numeral or LP
     mark lands, and where a point's tip opens. Three things wanting one corner.
     The marks and the tip are information; this is a control used once in a
     while, so it is the one that waits to be asked for. Visible on hovering the
     chart, or when it has keyboard focus. */
  .lpc-range{position:absolute;top:4px;right:5px;display:flex;gap:2px;z-index:3;
    opacity:0;pointer-events:none;transition:opacity .13s ease}
  .lpc-plot:hover .lpc-range,.lpc-range:focus-within{opacity:1;pointer-events:auto}
  /* except while a point is being read — the tip wants that corner and you asked
     for the tip. Outranks the hover rule above on specificity. */
  .lpc-plot:has(.lpc-pt:hover) .lpc-range{opacity:0;pointer-events:none}
  .lpc-range button{font-size:calc(9px*var(--ts));font-weight:700;letter-spacing:.06em;line-height:1;
    padding:3px 5px;border-radius:var(--r-xs);border:1px solid transparent;background:#ffffff0d;
    color:var(--faint);box-shadow:none}
  .lpc-range button:hover{background:#ffffff1c;border-color:transparent;color:var(--text)}
  .lpc-range button.on{background:color-mix(in srgb,var(--teal) 22%,transparent);color:var(--teal-txt)}
  /* The line passes under the captions wherever it climbs through a band's
     corner, and ran straight through them: "GOLD" read as a line with letters
     on it. A halo in the plot's own dark lifts the letters off it. */
  .lpc-plot .lpc-lab,.lpc-plot .lpc-sub,.lpc-plot .lpc-peak{text-shadow:0 0 1px var(--bg),0 0 2px var(--bg),0 0 3px var(--bg),0 0 6px var(--bg)}
  /* Clear of the first point, which sits 1.6% in (X_IN in rankChart) with a 5px
     radius: at 6px flat the dot landed on the caption's first letter. */
  .lpc-plot .lpc-lab{position:absolute;left:calc(1.6% + 9px);font-size:calc(9px*var(--ts));font-weight:600;letter-spacing:.09em;
    text-transform:uppercase;pointer-events:none;opacity:.82;line-height:1}
  /* the ceiling the line is climbing back towards; left lane, with the band
     captions, and dropped rather than stacked when one is already there */
  .lpc-plot .lpc-peak{position:absolute;left:50%;transform:translateX(-50%);
    font-size:calc(9px*var(--ts));font-weight:700;letter-spacing:.12em;
    text-transform:uppercase;pointer-events:none;line-height:1;color:var(--gold-txt);opacity:.9}
  /* division numerals below Master, LP marks above it — opposite side to the tier
     names so the two never collide */
  .lpc-plot .lpc-sub{position:absolute;right:6px;font-size:calc(9px*var(--ts));font-weight:600;letter-spacing:.06em;
    color:#fff;opacity:.5;pointer-events:none;line-height:1;font-variant-numeric:tabular-nums}
  /* Each point owns the slice of the chart nearest to it, and the slices tile the
     chart exactly. The first version centred a fixed-width column on every point,
     which meant the outermost ones hung ~56 px past the edge of the chart — far
     enough to cover part of the neighbouring card and steal its hover. */
  /* The column positions things and draws the guide; it is no longer the thing
     you hover. That is .lpc-hit, a band around the dot — pointer-events on a
     child still give the column its :hover, so the guide, the dot's pop and the
     tip all keep working off the same selector. */
  .lpc-pt{position:absolute;top:0;bottom:0;pointer-events:none}
  /* As wide as the column allows (which narrows by itself when checks are dense)
     and 26px tall, centred on the dot — instead of the full height of the plot. */
  .lpc-hit{position:absolute;left:var(--hl,0);right:var(--hr,0);top:var(--y);
    height:26px;margin-top:-13px;pointer-events:auto;cursor:pointer}
  /* the guide makes it obvious which check you're reading, not just which dot */
  .lpc-pt::before{content:"";position:absolute;left:var(--x);top:0;bottom:0;width:1px;margin-left:-.5px;
    background:#ffffff2b;opacity:0;transition:opacity .13s ease}
  .lpc-pt:hover::before{opacity:1}
  .lpc-pt>b{position:absolute;left:var(--x);width:0;height:0}
  .lpc-pt i{position:absolute;left:0;top:0;width:7px;height:7px;margin:-3.5px 0 0 -3.5px;border-radius:50%;
    box-shadow:0 0 0 2px var(--panel);transition:transform .15s cubic-bezier(.34,1.56,.64,1)}
  .lpc-pt:hover i{transform:scale(1.55)}
  .lpc.dense .lpc-pt:not(.now):not(:hover) i{transform:scale(0)}
  /* Opaque, not glass. This is the one surface that is always read against a busy
     background — the line, the band captions and the LP numerals all pass under
     it — and at 96% the line showed straight through the text. */
  .lpc-tip{position:absolute;left:50%;bottom:10px;transform:translateX(-50%);z-index:6;
    opacity:0;pointer-events:none;transition:opacity .13s ease;
    white-space:nowrap;font-size:calc(11px*var(--ts));font-weight:500;line-height:1.35;color:var(--text);
    padding:6px 9px;border-radius:var(--r-sm);background:var(--panel2);border:1px solid var(--line2);
    box-shadow:var(--sh-3)}
  .lpc-tip em{display:block;font-style:normal;font-size:calc(10px*var(--ts));color:var(--faint);margin-top:1px}
  .lpc-tip.l{left:-6px;transform:none}
  .lpc-tip.r{left:auto;right:-6px;transform:none}
  /* Below the dot when the dot sits too near the top for a tip to fit above it.
     That corner is also where the range switcher lives, so a high point — which
     the newest check on a climbing account usually is — used to hang its tip
     straight over D/W/M. */
  .lpc-tip.hi{bottom:auto;top:12px}
  .lpc-pt:hover .lpc-tip{opacity:1}
  .stale{color:var(--warn-txt)}
  #toast button{margin-left:12px;font-size:calc(12px*var(--ts));padding:3px 10px;color:var(--teal-txt)}
  .meta{font-size:calc(13px*var(--ts));color:var(--muted);line-height:1.65}
  .meta b{color:var(--text);font-weight:500}
  .meta .g{color:var(--gold-txt)}
  .ctags{display:flex;gap:6px;flex-wrap:wrap}
  .ctags span{font-size:calc(11px*var(--ts));padding:3px 9px;border-radius:var(--r-xs);background:#ffffff0d;border:1px solid #ffffff1f;color:var(--muted)}
  /* the role is read from recent games, not typed by you, so it is set apart from your tags */
  .ctags .role-tag{color:var(--teal-txt);border-color:color-mix(in srgb,var(--teal) 35%,transparent);background:color-mix(in srgb,var(--teal) 8%,transparent)}
  .login{background:#00000038;border:1px solid var(--line);border-radius:var(--r-md);padding:12px;font-size:calc(13px*var(--ts));display:flex;flex-direction:column;gap:9px}
  .login .k{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.13em;text-transform:uppercase;color:var(--faint);display:block;margin-bottom:7px}
  .lrow{display:flex;justify-content:space-between;align-items:center;gap:10px}
  .lrow .val{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .lrow .mono{font-family:ui-monospace,'SF Mono',Consolas,monospace;letter-spacing:.01em}
  .lrow .none{color:var(--faint)}
  /* The recovery facts are labelled; the credentials above them are not, because
     a username and a password are obvious from their shape and these are not. */
  .lrow .lk{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.1em;text-transform:uppercase;
    color:var(--faint);margin-right:9px}
  .lrow .lage{font-style:normal;font-size:calc(11px*var(--ts));color:var(--faint);margin-left:9px}
  /* what the password check-up found about this one, where you would change it */
  .lwarn{display:flex;align-items:flex-start;gap:7px;font-size:calc(12px*var(--ts));line-height:1.4;color:var(--gold-txt)}
  .lwarn .ico{width:14px;height:14px;flex-shrink:0;margin-top:1px}
  .lwarn.bad,.pwc-row .why .bad{color:var(--danger)}
  .pwc-leak{display:flex;align-items:center;gap:8px 14px;flex-wrap:wrap;margin-top:12px}
  .pwc-leak .hint{flex:1 1 260px;margin:0}
  .pwc-list{display:flex;flex-direction:column;gap:6px;margin-top:10px}
  .pwc-list:empty{display:none}
  .pwc-row{display:flex;align-items:center;gap:12px;padding:8px 10px 8px 12px;
    border:1px solid var(--line);border-radius:var(--r-sm);background:#00000026}
  /* name and reason side by side, the reason under the name when there's no room;
     Change stays beside them either way */
  .pwc-row .t{flex:1;min-width:0;display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 12px}
  .pwc-row .n{font-weight:600;color:var(--text);min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .pwc-row .why{min-width:0;font-size:calc(12px*var(--ts));color:var(--gold-txt)}
  .pwc-row button{flex-shrink:0;padding:5px 11px;font-size:calc(12px*var(--ts))}
  /* Beside the name in all three layouts, because the point of the flag is that
     you spot it without having opened anything. */
  /* Drawn only when there is a goal. An account with nothing to climb towards
     should not be shown an empty progress bar to feel bad about. */
  .goal{display:flex;flex-direction:column;gap:5px;margin-top:-3px}
  .goal-h{display:flex;align-items:baseline;gap:8px;font-size:calc(11px*var(--ts))}
  .goal-h .k{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--faint)}
  .goal-t{font-weight:700;letter-spacing:-.01em;font-size:calc(12px*var(--ts))}
  .goal-r{margin-left:auto;color:var(--faint);font-variant-numeric:tabular-nums;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .goal-bar{height:4px;border-radius:1px;background:#ffffff12;overflow:hidden}
  .goal-bar i{display:block;height:100%;border-radius:1px;transition:width .5s cubic-bezier(.22,1,.36,1)}
  .goal.hit .goal-r{color:var(--good);font-weight:600}
  @media (prefers-reduced-motion:reduce){.goal-bar i{transition:none}}
  .flagmark{display:inline-flex;flex-shrink:0;color:var(--danger)}
  .flagmark .ico{width:12px;height:12px;stroke-width:2.2}
  .t-nm .flagmark .ico,.rw-nm .flagmark .ico{width:11px;height:11px}
  .lbtns{display:flex;gap:5px;flex-shrink:0}
  .lbtns button{font-size:calc(11px*var(--ts));padding:4px 9px}
  .lbtns button.copied,button.copied{color:var(--good);border-color:color-mix(in srgb,var(--good) 45%,transparent);gap:4px}
  button.copied .ico{width:11px;height:11px;stroke-width:2.6}
  .login .lseq{width:100%;font-size:calc(12px*var(--ts));font-weight:600;padding:8px 10px;color:var(--teal-txt);
    border-color:color-mix(in srgb,var(--teal) 45%,transparent);background:color-mix(in srgb,var(--teal) 10%,transparent)}
  .login .lseq:hover{border-color:var(--teal);background:color-mix(in srgb,var(--teal) 18%,transparent);color:var(--teal-txt)}
  .c-foot{display:flex;flex-direction:column;align-items:stretch;gap:11px;
    border-top:1px solid var(--line);padding-top:12px;margin-top:auto}
  .c-meta{display:flex;align-items:center;gap:9px;flex-wrap:wrap;min-width:0}
  /* was a bare run-on caption floating above the buttons; now a dot-led timestamp
     with the source as its own tag, so it reads as a deliberate meta line */
  .upd{font-size:calc(11px*var(--ts));color:var(--faint);letter-spacing:.015em;display:inline-flex;align-items:center;gap:7px;min-width:0}
  .upd::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.55;flex-shrink:0}
  .upd.stale{color:var(--warn-txt)}
  /* Out of date: amber (it used to be red; see .cmd-count .warn), with a warning
     mark in place of the dot. It was a ⚠ character, which some systems draw as a
     yellow emoji; this one is drawn. */
  .upd.stale::before{display:none}
  .upd .ico{width:12px;height:12px;flex-shrink:0}
  /* asOf is sometimes a source ("backend", a proxy name) and sometimes a date
     phrase op.gg printed ("updated today") — so no uppercasing, which would
     shout at a sentence fragment */
  .src{font-size:calc(10px*var(--ts));font-weight:500;letter-spacing:.01em;color:var(--faint);
    padding:2px 8px;border-radius:var(--r-xs);background:#ffffff0d;border:1px solid #ffffff1c;white-space:nowrap}
  /* 11.5px type in 6px of padding is a 26px target, and there were five of them
     in a row. Mobile already got 40px; the desktop was left with whatever the
     text happened to measure. */
  .acts{display:flex;gap:7px;flex-wrap:wrap}
  .acts button,.acts a{font-size:calc(12px*var(--ts));padding:7px 12px;min-height:32px;text-decoration:none}
  .c-more{min-width:36px;padding:7px 10px;font-size:calc(16px*var(--ts));line-height:1;color:var(--muted);
    letter-spacing:.08em}
  .c-more:hover{color:var(--text)}
  .c-more.on{background-color:var(--panel3);border-color:var(--line2);color:var(--text)}
  /* Once op.gg and dpm moved into the menu this row was two buttons and a small
     square, all shoved against the left edge with a third of the card empty
     beside them. The pair share the width and the ⋯ closes the row, so the
     footer reads as one bar rather than as three things put down next to each
     other. Card only: a list row's action bar has seven of these and wants its
     natural widths. */
  .c-foot .acts{gap:8px}
  .c-foot .acts>button:not(.c-more){flex:1 1 0;min-width:0}
  .c-foot .acts>.c-more{flex:0 0 auto}

  /* ---- the card's ⋯ menu: one fixed element, shared by every card ---- */
  .card-menu{position:fixed;z-index:70;width:232px;max-width:calc(100vw - 16px)}
  /* Tall enough that the whole menu fits without scrolling on any normal screen —
     a scrollbar here clipped the first row and made the list look truncated. */
  .cm-in{padding:5px;display:flex;flex-direction:column;gap:0;
    max-height:min(82vh,540px);overflow:auto;overscroll-behavior:contain}
  /* Named groups, not bare hairlines. A rule says "these are apart"; it never says
     what either side of it is. Same lockup as the list view's tier bands. */
  .cm-g{display:flex;align-items:center;gap:8px;padding:7px 11px 3px;
    font-size:calc(10px*var(--ts));font-weight:700;line-height:1;letter-spacing:.14em;text-transform:uppercase;
    color:var(--muted);user-select:none}
  .cm-g::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--line2),transparent)}
  .cm-g:first-child{padding-top:3px}
  /* A menu is read, item by item, over a busy card — on an iPad it was grey
     words on a near-black panel the card behind still showed through. The
     panel is solid now, the items are in the text colour and their marks one
     step down, and hover keeps its own tint and teal mark. */
  .card-menu>.hx-in,#moreMenu>.hx-in{background:var(--panel2)}
  .card-menu button,.card-menu .cm-link{width:100%;justify-content:flex-start;gap:10px;
    font-size:calc(13px*var(--ts));font-weight:500;padding:8px 11px;min-height:34px;border-radius:var(--r-sm);
    background:none;border:1px solid transparent;box-shadow:none;color:var(--text);
    text-decoration:none;display:inline-flex;align-items:center;cursor:pointer;text-align:left}
  .card-menu .ico{width:15px;height:15px;flex-shrink:0;color:var(--muted);
    transition:color .14s ease}
  .card-menu button:hover,.card-menu .cm-link:hover{background:#ffffff0f;border-color:transparent;color:var(--text)}
  .card-menu button:hover .ico,.card-menu .cm-link:hover .ico{color:var(--teal-txt)}
  .card-menu button:focus-visible,.card-menu .cm-link:focus-visible{outline-offset:-2px}
  .card-menu button.b-danger{color:var(--danger)}
  .card-menu button.b-danger .ico{color:color-mix(in srgb,var(--danger) 70%,transparent)}
  .card-menu button.b-danger:hover{background:color-mix(in srgb,var(--danger) 14%,transparent);color:var(--danger)}
  .card-menu button.b-danger:hover .ico{color:var(--danger)}
  .card-menu:not(.hidden){animation:menuIn .14s cubic-bezier(.22,1,.36,1) both}
  @keyframes menuIn{from{opacity:0;transform:translateY(4px) scale(.985)}to{opacity:1;transform:none}}
  @media (prefers-reduced-motion:reduce){.card-menu:not(.hidden){animation:none}}

  /* ---- ✎ Rank panel ---- */
  /* The panel is the rank window's body now, and .mdl-b carries no padding of its
     own — the account form gets its 22px from .formgrid. This is that measure. */
  .rankpanel{display:flex;flex-direction:column;gap:14px;padding:20px 22px}
  .rankpanel .k{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.13em;text-transform:uppercase;
    color:var(--faint);display:block;margin-bottom:8px}
  /* Named fields that wrap, rather than a row of bare boxes sized to the shortest
     thing they might hold. Wide enough that four digits and a spinner are not
     fighting for the same pixels. */
  /* Three to a row rather than however many happen to fit, so Current breaks
     where it means something — the rank on the first line, the record on the
     second — instead of landing on an arbitrary four-and-one. */
  .rk-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px 14px}
  @media (max-width:560px){.rk-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}
  .rk-f{display:flex;flex-direction:column;gap:5px;min-width:0}
  .rk-f>span{font-size:calc(11px*var(--ts));font-weight:500;letter-spacing:.005em;color:var(--muted)}
  .rk-f>select,.rk-f>input{width:100%}
  /* The native steppers sit over the digits at this size and are no use for a
     season's win count anyway — these are numbers you type, not nudge. */
  .rk-f input[type=number]{appearance:textfield;-moz-appearance:textfield;
    font-variant-numeric:tabular-nums}
  .rk-f input[type=number]::-webkit-outer-spin-button,
  .rk-f input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;appearance:none;margin:0}
  .rk-note{font-size:calc(11px*var(--ts));color:var(--faint);margin-top:9px;line-height:1.5}
  .rk-none{color:var(--faint);font-weight:450}
  /* The two folds run the full width of the window and re-pad their own contents
     back to the same measure, so a closed fold reads as a divider across it. */
  .rankpanel .sec{border-top:1px solid var(--line);margin:0 -22px}
  .rankpanel .sec-h{padding:12px 22px}
  .rankpanel .sec-b{padding:2px 22px 14px}
  .rankpanel .sec:last-child{border-bottom:1px solid var(--line)}
  /* DPM and OP.GG are the same kind of thing — an outbound profile link — so they
     look the same. The gold tint implied a hierarchy that isn't there. */
  .a-dpm{border:1px solid var(--edge);color:var(--text);font-weight:500;background-color:var(--panel2)}
  .a-dpm:hover{color:var(--text);border-color:var(--line2);background-color:var(--panel3)}
  /* The main action, and the one filled plate on a card: that is what makes it
     stand out, not a gloss or a glow. It wears the same satin as every button,
     the light turned up a little so it shows on a bright fill. */
  .a-login{background-color:var(--teal);background-image:var(--sheen-fill);border-color:var(--teal);color:var(--teal-ink);font-weight:700;
    box-shadow:var(--sh-fill)}
  .a-login:hover{background-color:color-mix(in srgb,var(--teal) 86%,#fff);border-color:color-mix(in srgb,var(--teal) 86%,#fff);color:var(--teal-ink)}

  /* One eyebrow. The small uppercase label over a figure is the most repeated
     element on the page — ACCOUNTS, PEAK, GOAL, CURRENT, RANK SPREAD — and it was
     set four sizes and seven trackings apart, from 9px to 10.5px and .1em to .16em,
     so neighbouring labels never quite lined up. Colour still says how loud each
     one is; the shape is now the same everywhere. */
  .stat .k,.dist .k,.dash-filter .k,.c-stats .k,.c-notes .k,.sec-n,.chp-h,.away-k,.pal-g,.login .k,.lrow .lk,
  .goal-h .k,.lvt-h .k,.rankpanel .k,.grp-h,.cm-g,.c-drawer{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.12em;text-transform:uppercase}

  /* ---- form + settings ---- */
  .formgrid{display:grid;grid-template-columns:1fr 1fr;gap:16px;padding:22px}
  .fw{grid-column:1/-1}
  .fld{display:flex;flex-direction:column;gap:6px}
  /* sentence case, not micro-caps — several of these labels are full sentences */
  .fld label{font-size:calc(12px*var(--ts));font-weight:500;letter-spacing:.005em;color:var(--muted);line-height:1.45}
  /* The strength meter held a 30px band open under the password even with no
     password in it, across both columns — the widest gap in the form, around
     nothing. The bar now sits in the grid's own gap and the word rides the label. */
  .pw-fld{position:relative}
  .pw-fld label{display:flex;justify-content:space-between;gap:10px}
  #pwMeterLabel{font-size:calc(11px*var(--ts));color:var(--faint)}
  #pwMeter{position:absolute;left:0;right:0;bottom:-9px;height:3px;display:flex;gap:4px}
  .hidden{display:none!important}
  .empty{padding:64px 24px;text-align:center;color:var(--muted);font-size:calc(14px*var(--ts));line-height:1.7}
  .empty .d{height:84px;margin-bottom:20px;display:flex;align-items:center;justify-content:center}
  .empty-gem{width:52px;height:52px;display:inline-block;border-radius:var(--r-xs);transform:rotate(45deg);
    border:1px solid color-mix(in srgb,var(--gold) 40%,transparent);
    background:color-mix(in srgb,var(--gold) 12%,var(--panel2))}
  .welcome{padding:56px 24px 52px}
  .wl-t{margin:0 0 8px;font-size:calc(20px*var(--ts));font-weight:600;letter-spacing:-.015em;color:var(--text);line-height:1.3}
  .wl-s{margin:0 auto;max-width:46ch;font-size:calc(14px*var(--ts));color:var(--muted);line-height:1.6}
  .wl-steps{list-style:none;margin:28px auto 0;padding:0;max-width:760px;display:grid;
    grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;text-align:left}
  .wl-steps li{display:grid;grid-template-columns:auto 1fr;grid-template-rows:24px auto;column-gap:12px;row-gap:2px;align-items:center;
    padding:14px 16px;border:1px solid var(--line);border-radius:var(--r-sm);background:var(--panel2)}
  .wl-n{grid-row:span 2;align-self:start;width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;
    border-radius:50%;border:1px solid var(--line2);font-size:calc(12px*var(--ts));font-weight:600;color:var(--muted);
    font-variant-numeric:tabular-nums}
  .wl-steps b{font-size:calc(13px*var(--ts));font-weight:600;color:var(--text)}
  .wl-steps li>span:last-child{align-self:start;font-size:calc(12.5px*var(--ts));color:var(--faint);line-height:1.5}
  .wl-acts{margin-top:26px;display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
  .lp-p{margin:0;font-size:calc(13px*var(--ts));line-height:1.55;color:var(--muted)}
  .lp-off{display:flex;align-items:center;gap:8px;font-size:calc(12px*var(--ts));color:var(--muted);cursor:pointer}
  .lp-off input{width:auto}
  .wl-note{margin:22px auto 0;max-width:60ch;display:flex;gap:8px;align-items:flex-start;justify-content:center;
    font-size:calc(12px*var(--ts));color:var(--faint);line-height:1.55;text-align:left}
  .wl-note .ico{width:13px;height:13px;margin-top:2px;flex-shrink:0}
  @media (max-width:640px){.wl-steps{grid-template-columns:1fr}}
  .skip-link{position:fixed;left:16px;top:-80px;z-index:100;padding:10px 16px;border-radius:var(--r-sm);
    background:var(--panel2);border:1px solid var(--line2);color:var(--text);font-weight:600;text-decoration:none;
    box-shadow:var(--sh-2);transition:top .15s ease}
  .skip-link:focus{top:16px}
  #grid:focus{outline:none}
  #sFootHint.dirty{color:var(--warn-txt)}
  .count{margin-top:30px;text-align:center;font-size:calc(12px*var(--ts));color:var(--faint)}
  .count a{color:var(--gold-txt);text-decoration:none;border-bottom:1px solid transparent;transition:border-color .15s}
  .count a:hover{border-bottom-color:currentColor}
  /* The version, hovering for what changed in it. Tabular figures so it doesn't
     jiggle between releases, and a help cursor because there is something to read. */
  .ver{font-variant-numeric:tabular-nums;letter-spacing:.02em}
  .ver[title]{cursor:help;border-bottom:1px dotted var(--line2)}

  /* ---- modal panels ----
     As inline panels these shoved the entire grid down the page — open one on a
     full vault and the cards you were looking at are three screens away. They
     open over the page instead.
     The account form was the last one left inline, on the reasoning that it comes
     from the console and acts on the collection underneath. It doesn't: it is
     opened just as often from a card's ⋯ menu to edit that card, and pushing the
     grid down means the account you are editing walks off the screen the moment
     you start. Sixteen fields is a page of its own either way.
     Bulk add is the one panel still inline. */
  .modal{position:fixed;inset:0;z-index:60;overflow:auto;overscroll-behavior:contain;
    display:flex;align-items:flex-start;justify-content:center;
    padding:clamp(14px,5vh,60px) clamp(12px,4vw,32px)}
  .modal::before{content:"";position:fixed;inset:0;background:#05070bd1;
    backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);
    animation:backIn .22s ease both}
  /* margin:auto centres it while short, and lets it grow past the viewport and
     scroll the overlay when the content is genuinely tall */
  .mdl{position:relative;width:100%;max-width:860px;margin:auto}
  .mdl.narrow{max-width:680px}
  /* header and footer stay put, the middle scrolls — on a laptop the Save button
     was below the fold of a panel that had already pushed the page around */
  .mdl-in{display:flex;flex-direction:column;max-height:calc(100vh - clamp(28px,10vh,120px))}
  .mdl-h{display:flex;align-items:center;gap:13px;flex-shrink:0;padding:17px 22px;
    border-bottom:1px solid var(--line);background:linear-gradient(180deg,#ffffff0a,transparent)}
  /* a window says what it is in plain words, at title size; the gold frame around it
     is the ornament */
  .mdl-h .t{font-size:calc(16px*var(--ts));font-weight:600;letter-spacing:-.01em;color:var(--text)}
  .mdl-h .s{font-size:calc(12px*var(--ts));color:var(--faint);margin-left:auto;margin-right:34px;
    min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .mdl-b{flex:1;min-height:0;overflow:auto;overflow-x:hidden}
  .mdl-f{display:flex;gap:9px;align-items:center;flex-wrap:wrap;flex-shrink:0;
    padding:14px 22px;border-top:1px solid var(--line);background:color-mix(in srgb,var(--bg) 72%,transparent)}
  .mdl-f .sp{margin-left:auto}
  /* a labelled group per concern, instead of eight unrelated fields in one grid */
  .grp{padding:20px 22px}
  .grp+.grp{border-top:1px solid var(--line)}
  .stabs{display:flex;gap:2px;flex-shrink:0;padding:0 14px;border-bottom:1px solid var(--line);
    overflow-x:auto;scrollbar-width:none}
  .stabs::-webkit-scrollbar{display:none}
  #settings .stabs button{background:none;border:0;border-radius:0;box-shadow:inset 0 -2px 0 transparent;
    padding:12px 10px 11px;font-size:calc(13px*var(--ts));font-weight:600;color:var(--muted);white-space:nowrap;flex-shrink:0}
  #settings .stabs button:hover{color:var(--text);background:none}
  #settings .stabs button[aria-selected="true"]{color:var(--gold-txt);box-shadow:inset 0 -2px 0 var(--gold)}
  /* only the open tab's groups; the line between groups only between visible ones */
  #settings .grp[data-tab]{display:none;border-top:0}
  #settings[data-tab="look"] .grp[data-tab="look"],#settings[data-tab="checks"] .grp[data-tab="checks"],
  #settings[data-tab="alerts"] .grp[data-tab="alerts"],#settings[data-tab="security"] .grp[data-tab="security"],
  #settings[data-tab="sync"] .grp[data-tab="sync"],#settings[data-tab="backup"] .grp[data-tab="backup"]{display:block}
  #settings .grp[data-tab="checks"]+.grp[data-tab="checks"]{border-top:1px solid var(--line)}
  .grp-h.tab-same{display:none}
  .bk-acts{display:flex;flex-wrap:wrap;gap:8px}
  /* streamer mode: blurred where it stands, so the layout does not jump */
  body.streamer :is(.sens,.login .val,.c-notes textarea),
  body.streamer :is(#fName,#fTag,#fLogin,#fPass,#fEmail,#fNotes,#fCreated,#fBirth,#baList,.nf-sens){filter:blur(7px);user-select:none}
  .streamer-pill{display:inline-flex;align-items:center;gap:7px;padding:5px 11px;border-radius:var(--r-sm);font-size:calc(12px*var(--ts));font-weight:600;
    color:#ffb4b4;border:1px solid #ff6b6b66;background:#ff6b6b17;box-shadow:none}
  .streamer-pill:hover{color:#fff;border-color:#ff6b6baa;background:#ff6b6b26}
  .streamer-pill i{width:7px;height:7px;border-radius:50%;background:#ff5b5b;box-shadow:0 0 8px #ff5b5b}
  .part-chks{display:flex;flex-wrap:wrap;gap:6px}
  .part-chks label{display:inline-flex;align-items:center;gap:7px;padding:6px 11px;border:1px solid var(--edge);
    border-radius:var(--r-sm);background:var(--panel2);font-size:calc(13px*var(--ts));color:var(--muted);cursor:pointer}
  .part-chks label:has(input:checked){color:var(--text);border-color:var(--line2)}
  .part-chks input{width:auto;margin:0}
  #settings .link-btn{display:inline;padding:0;border:0;background:none;box-shadow:none;min-height:0;
    color:var(--teal-txt);font:inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer}
  html[data-ts="small"]{--ts:.9}
  html[data-ts="large"]{--ts:1.15}
  /* a three-way choice in Settings: one row, the chosen one lit */
  .opt-seg{display:inline-flex;border:1px solid var(--edge);border-radius:var(--r-sm);overflow:hidden;background:var(--sheen),var(--panel2);box-shadow:var(--sh-btn)}
  #settings .opt-seg button{border:0;border-radius:0;background:none;box-shadow:none;padding:7px 16px;font-size:calc(13px*var(--ts));font-weight:600;color:var(--muted)}
  #settings .opt-seg button+button{border-left:1px solid var(--line)}
  #settings .opt-seg button:hover{color:var(--text);background:#ffffff0d}
  #settings .opt-seg button[aria-checked="true"]{color:var(--gold-txt);background:color-mix(in srgb,var(--gold) 14%,transparent)}
  .grp-h{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.16em;text-transform:uppercase;
    color:var(--muted);margin-bottom:14px;display:flex;align-items:center;gap:9px}
  .grp-h::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--line),transparent)}
  .grp .formgrid{padding:0;gap:15px}
  .hint{font-size:calc(12px*var(--ts));color:var(--faint);line-height:1.55}
  /* align-items:flex-start so a label that wraps to two lines still starts level
     with its checkbox instead of floating half a line below it */
  /* Checkbox option rows: the label grows so a long line wraps cleanly.
     Inline control rows (refresh interval, auto-lock) must not stretch — that
     shoved the number field into the middle of a wide empty gap. */
  .sub-fld{padding-left:24px;margin-top:9px;display:flex;align-items:flex-start;gap:9px;flex-wrap:wrap}
  .sub-fld>label,.sub-fld>span{flex:1;min-width:0}
  .sub-fld.inline{align-items:center;gap:10px}
  .sub-fld.inline>label,.sub-fld.inline>span{flex:none;color:var(--muted);font-size:calc(13px*var(--ts))}
  .sub-fld.inline select,.sub-fld.inline input[type=number]{width:auto;min-width:0;padding:6px 10px;font-size:calc(13px*var(--ts))}
  /* the arrow is drawn 10px in from the right and is 13px wide: the shorthand
     above took the room select{} leaves for it, and "Off — never" ran under it */
  .sub-fld.inline select{padding-right:30px}
  .sub-fld input[type=checkbox]{margin-top:3px}
  .chk{display:flex;align-items:center;gap:9px}
  .chk label{font-size:calc(13px*var(--ts));color:var(--text);line-height:1.45}
  /* ---- accent preview ----
     A colour swatch shows the colour; it cannot show what the colour does. The page
     sits behind this panel's blur, and a sample *under* the swatches is exactly where
     the browser's colour popup opens — covered the moment you ask. So the sample is
     a fixed miniature of the real homepage (plus a card beside it) that is placed
     next to the colour popup while a picker is open.
     Built from the same custom properties as the real thing: applyAccent() sets
     them on :root, so this follows every drag without a line of code behind it.
     inert: a picture of the app, not a second app. */
  .acc-swatches{display:flex;gap:22px;flex-wrap:wrap;align-items:center}
  .acc-swatch{display:flex;gap:10px;align-items:center}
  /* Colour fields larger than the reset beside them — the reset stays as it is;
     the swatch is the thing you are aiming for when you want the picker. */
  .acc-swatch input[type=color]{width:52px;height:40px;padding:3px;cursor:pointer;
    flex-shrink:0}
  .acc-prev{display:none;position:fixed;z-index:70;width:min(560px,calc(100vw - 24px));
    padding:0;border:1px solid var(--line2);border-radius:var(--r-md);pointer-events:none;
    background:
      radial-gradient(ellipse 80% 50% at 10% -10%,color-mix(in srgb,var(--gold) 14%,transparent),transparent 55%),
      radial-gradient(ellipse 60% 40% at 100% 0%,color-mix(in srgb,var(--teal) 10%,transparent),transparent 50%),
      var(--bg);
    box-shadow:0 18px 50px -16px #000000c0,inset 0 1px 0 #ffffff0a;
    flex-direction:row;align-items:stretch;gap:0;overflow:hidden;
    animation:accPrevIn .22s cubic-bezier(.22,1,.36,1) both}
  .acc-prev.show{display:flex}
  @keyframes accPrevIn{from{opacity:0;transform:translateX(-8px)}to{opacity:1;transform:none}}
  .acc-prev-home{flex:1 1 55%;min-width:0;display:flex;flex-direction:column;
    border-right:1px solid var(--line)}
  .acc-prev-top{display:flex;align-items:center;gap:10px;padding:12px 14px;
    border-bottom:1px solid var(--line);background:color-mix(in srgb,var(--panel) 72%,transparent)}
  /* The miniature is only worth anything if it looks like the page it stands in
     for: the turned gem, display-face actions, the rank as the card sets it. */
  .acc-prev-mark{width:20px;height:20px;margin:4px;border-radius:2px;flex-shrink:0;transform:rotate(45deg);
    display:flex;align-items:center;justify-content:center;
    background:var(--panel2);
    box-shadow:inset 0 0 0 1px var(--gold),0 0 0 2px var(--bg),0 0 0 3px color-mix(in srgb,var(--gold) 45%,var(--line))}
  .acc-prev-mark i{display:block;width:8px;height:8px;border-radius:1px;background:var(--gold)}
  .acc-prev-brand{min-width:0}
  .acc-prev-t{font-family:'Cinzel',Georgia,serif;letter-spacing:.1em;text-transform:uppercase;
    font-size:calc(12px*var(--ts));color:var(--gold-txt);line-height:1.2}
  .acc-prev-sub{font-size:calc(10px*var(--ts));color:var(--faint);margin-top:2px}
  .acc-prev-sub b{color:var(--gold-txt);font-weight:600}
  .acc-prev-rib{display:flex;gap:2px;height:14px;padding:8px 14px 0}
  .acc-prev-rib i{flex:1;border-radius:1px;background:color-mix(in srgb,var(--c) 62%,var(--bg))}
  .acc-prev-cmd{display:flex;gap:6px;flex-wrap:wrap;padding:10px 14px;
    border-bottom:1px solid var(--line)}
  .acc-prev-cmd button,.acc-prev-card .bar button{padding:5px 9px;font:600 calc(11px*var(--ts))/1.2 'Inter',system-ui,sans-serif}
  .acc-prev-tools{display:flex;gap:6px;align-items:center;padding:10px 14px 12px}
  .acc-prev-tools input{flex:1;min-width:0;font-size:calc(11px*var(--ts));padding:6px 9px;
    border-color:color-mix(in srgb,var(--teal) 60%,transparent);
    box-shadow:0 0 0 3px color-mix(in srgb,var(--teal) 13%,transparent);
    background-color:var(--panel3)}
  .acc-prev-tools button{padding:5px 8px;font-size:calc(11px*var(--ts))}
  .acc-prev-tools button.on{color:var(--star);
    border-color:color-mix(in srgb,var(--star) 55%,transparent)}
  .acc-prev-side{flex:1 1 42%;min-width:0;padding:12px;display:flex;flex-direction:column;
    background:color-mix(in srgb,var(--panel) 40%,transparent)}
  .acc-prev-card{padding:11px 12px;border:1px solid var(--line);border-radius:var(--r-sm);
    background:var(--panel);box-shadow:inset 0 1px 0 #ffffff08;flex:1}
  .acc-prev-card-h{display:flex;flex-wrap:wrap;align-items:center;gap:4px 7px;margin-bottom:6px}
  .acc-prev-nm{font-size:calc(13px*var(--ts));font-weight:600;color:var(--text);white-space:nowrap}
  .acc-prev-star{color:var(--star);font-size:calc(13px*var(--ts));line-height:1;margin-left:auto}
  .acc-prev-rk{flex-basis:100%;font:700 calc(14px*var(--ts))/1.1 'Inter',system-ui,sans-serif;letter-spacing:-.01em;color:#7ea6f0}
  .acc-prev-rk small{font:600 calc(11px*var(--ts)) Inter,system-ui,sans-serif;text-transform:none;color:var(--muted);margin-left:4px}
  .acc-prev-wl{font-size:calc(11px*var(--ts));color:var(--muted);margin-bottom:8px}
  .acc-prev-card .bar{gap:6px}
  .acc-reset{width:27px;height:27px;padding:0;font-size:calc(13px*var(--ts));color:var(--muted)}
  .acc-reset:hover{color:var(--text)}
  @media (max-width:620px){
    .acc-prev{flex-direction:column;width:min(340px,calc(100vw - 24px))}
    .acc-prev-home{border-right:0;border-bottom:1px solid var(--line)}
  }
  /* The viewport's scroll container is the root element, not <body>, so locking
     only <body> leaves the page scrolling behind the modal in most browsers. */
  body.modal-open{overflow:hidden}
  html:has(body.modal-open){overflow:hidden}
  /* and reserve the scrollbar's width at all times, so taking it away doesn't
     shunt the whole layout sideways as the modal opens */
  html{scrollbar-gutter:stable}
  /* On a phone a centred card floating in a dimmed page is the wrong shape — it
     leaves a dead band of backdrop under the footer and wastes the width. Full
     sheet instead: the header and footer pin to the screen edges and only the
     middle scrolls. dvh, not vh, so the browser's own collapsing toolbar doesn't
     push the footer out of reach. */
  @media (max-width:620px){
    .modal{padding:0;align-items:stretch}
    .mdl{max-width:none;margin:0}
    .mdl.hx{border-radius:0;padding:0;box-shadow:none}
    .mdl-in{max-height:none;min-height:100vh;min-height:100dvh;border-radius:0}
    .mdl-h .s{display:none}
    /* …except in the rank window, where that line is not a caption: it is which
       account you are editing. Hiding it put the phone back in the state the
       desktop had before it was fixed — a rank form with no name on it. It goes
       under the title instead of beside it. */
    :is(#rankModal,#artPicker) .mdl-h{flex-wrap:wrap}
    :is(#rankModal,#artPicker) .mdl-h .s{display:block;width:100%;margin:3px 34px 0 0}
    .grp,.mdl-h,.mdl-f{padding-left:16px;padding-right:16px}
  }

  /* ---- toast ---- */
  /* above the modals (z 60): "Vault password set" is confirmed from inside
     Settings, and behind the backdrop it would never be seen */
  /* left:50% with no width shrinks to the half of the viewport right of that point —
     195px on a phone, which made every longer toast a tall pill of short lines. As
     wide as its text, up to the screen less its gutters, and no wider than the
     measure a desktop always got by accident. */
  #toast{position:fixed;bottom:26px;left:50%;transform:translateX(-50%);z-index:80;display:none;
    width:max-content;max-width:min(720px,calc(100vw - 32px));
    background:color-mix(in srgb,var(--panel2) 95%,transparent);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);
    border:1px solid var(--line2);border-radius:var(--r-md);color:var(--text);
    padding:11px 20px;font-size:calc(13px*var(--ts));font-weight:500;box-shadow:var(--sh-3)}

  .spin{display:inline-block;animation:sp 1s linear infinite}
  @keyframes sp{to{transform:rotate(360deg)}}

  /* ---- lock screen ----
     The one screen that is nothing but the brand, and it was a bare form pinned
     near the top: no mark, no name, a button sized to its word. It is a gate now —
     the gem, the wordmark, a gold rule, then one field and one full-width action. */
  #lock{max-width:400px;margin:min(14vh,124px) auto 0}
  .lock-in{display:flex;flex-direction:column;align-items:center;text-align:center;padding:40px 34px 32px}
  .lock-in .brand-mark{margin:6px 0 24px}
  .lock-wm{font-size:calc(30px*var(--ts))}
  .lock-sub{margin-top:8px;font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--faint)}
  .lock-in .hx-rule{margin:24px 0 20px}
  .lock-k{font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--gold-txt)}
  .lock-txt{font-size:calc(13px*var(--ts));color:var(--muted);margin:8px 0 18px;line-height:1.6}
  #lockPass{width:100%}
  .lock-err{align-self:stretch;text-align:left;color:var(--danger);font-size:calc(12px*var(--ts));margin-top:8px}
  .lock-go{width:100%;margin-top:14px;padding:11px 16px}
  .lock-pk{width:100%;margin-top:8px;padding:10px 16px;color:var(--teal-txt)}
  @media (max-width:440px){#lock{margin-top:8vh}.lock-in{padding:34px 22px 26px}.lock-wm{font-size:calc(26px*var(--ts))}}

  /* ---- entrances ----
     Panels toggle .hidden (display:none), which can't be transitioned. Taking an
     element out of display:none restarts its animations, so a rule scoped to the
     visible state fires exactly once per open. Closing stays instant on purpose:
     deferring it would mean .hidden lands a beat after the click, and both the UI
     and the tests read that class straight away. */
  @keyframes panelIn{from{opacity:0;transform:translateY(-10px) scale(.985)}to{opacity:1;transform:none}}
  #bulkAdd:not(.hidden),#lock:not(.hidden){
    animation:panelIn .3s cubic-bezier(.22,1,.36,1) both}
  /* A modal arrives from further away and settles, rather than sliding down from
     the top edge the way the inline panels do — it is replacing the page's
     attention, not adding a row to it. */
  @keyframes backIn{from{opacity:0}to{opacity:1}}
  @keyframes modalIn{from{opacity:0;transform:translateY(14px) scale(.97)}to{opacity:1;transform:none}}
  .modal:not(.hidden) .mdl{animation:modalIn .32s cubic-bezier(.22,1,.36,1) both}
  @keyframes menuIn{from{opacity:0;transform:translateY(-6px) scale(.94)}to{opacity:1;transform:none}}
  #moreMenu:not(.hidden){transform-origin:top right;animation:menuIn .17s cubic-bezier(.22,1,.36,1) both}
  /* Leaving the way they came. A window, a menu and the toast each rise in, and
     each used to cut to nothing on the way out: a hard edge at the end of every
     action. They fade now, quicker than they arrive, since closing is the user
     moving on. allow-discrete holds display:none back until the fade is done; the
     .hidden class still lands at once, so nothing can be clicked while it fades
     and every check for an open window answers straight away. Where a browser
     cannot hold display back, closing stays the cut it always was.
     Keyframes, not a transition, for the windows and menus: their way in is an
     animation, and a transition will not start on a property an animation held a
     moment before, so a faded menu just vanished and then sat there unseen for
     the length of the fade. A hidden one at page load is display:none and never
     runs its exit; it only plays on the way from open to closed. */
  @keyframes fadeOut{to{opacity:0}}
  @keyframes modalOut{to{opacity:0;transform:translateY(6px) scale(.985)}}
  @keyframes menuOut{to{opacity:0;transform:translateY(3px) scale(.985)}}
  @supports (transition-behavior:allow-discrete){
    .modal,.card-menu,#moreMenu{transition:display .15s allow-discrete}
    .modal.hidden{animation:fadeOut .15s ease both;pointer-events:none}
    .modal.hidden .mdl{animation:modalOut .15s ease both}
    .card-menu.hidden,#moreMenu.hidden{animation:menuOut .13s ease both;pointer-events:none}
    #toast{transition:opacity .18s ease,display .18s allow-discrete}
  }
  @keyframes dashIn{from{opacity:0;transform:translateY(-12px) scaleY(.94)}to{opacity:1;transform:none}}
  .hx.dash-in{transform-origin:top center;animation:dashIn .32s cubic-bezier(.22,1,.36,1) both}
  @keyframes barIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
  #previewBanner{display:flex;align-items:center;justify-content:space-between;gap:10px;
    border:1px solid color-mix(in srgb,var(--gold) 42%,transparent);background:color-mix(in srgb,var(--gold) 9%,transparent);
    color:var(--text);padding:7px 8px 7px 14px;border-radius:var(--r-md);font-size:calc(13px*var(--ts));margin-bottom:14px}
  #previewBanner>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  #previewBanner>button{flex:0 0 auto;padding:5px 12px;min-height:30px}
  #bulkbar:not(.hidden),#previewBanner:not(.hidden),.banner.show{animation:barIn .26s cubic-bezier(.22,1,.36,1) both}

  /* Card sections open by re-rendering the whole grid, so only the block flagged
     by `reveal` animates — see the note on that variable. */
  @keyframes sectionIn{from{opacity:0;transform:translateY(-7px) scaleY(.94)}to{opacity:1;transform:none}}
  .login.reveal,.acts.reveal,.c-notes.reveal{transform-origin:top center;animation:sectionIn .24s cubic-bezier(.22,1,.36,1) both}
  .login.reveal .lrow{animation:rowIn .3s cubic-bezier(.22,1,.36,1) both}
  .login.reveal .lrow:nth-child(2){animation-delay:.045s}
  .login.reveal .lrow:nth-child(3){animation-delay:.09s}
  @keyframes rowIn{from{opacity:0;transform:translateX(-5px)}to{opacity:1;transform:none}}
  @keyframes starPop{0%{transform:scale(1)}45%{transform:scale(1.45) rotate(9deg)}100%{transform:scale(1)}}
  .star.reveal{animation:starPop .42s cubic-bezier(.34,1.56,.64,1)}

  /* ---- micro-interactions ---- */
  .a-login .ico,.acts .ico{transition:transform .22s cubic-bezier(.22,1,.36,1)}
  .a-login:hover .ico{transform:scale(1.16)}
  .acts button:hover .ico{transform:rotate(-12deg)}
  .acts button[data-act="check"]:hover .ico{transform:rotate(180deg)}
  .cmp{padding:22px 22px 6px}
  .cmp-head{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:12px;align-items:start;margin-bottom:18px}
  .cmp-side{display:flex;flex-direction:column;align-items:center;text-align:center;gap:3px;min-width:0}
  .cmp-pic{width:56px;height:56px;border-radius:50%;margin-bottom:8px;display:flex;align-items:center;justify-content:center;
    background:linear-gradient(150deg,var(--panel3),var(--panel2));
    box-shadow:0 0 0 1px var(--gold-dim),0 0 0 3px var(--bg),0 0 0 4px color-mix(in srgb,var(--gold) 55%,transparent)}
  .cmp-pic img{width:100%;height:100%;border-radius:50%;object-fit:cover;display:block}
  .cmp-pic b{font-size:calc(20px*var(--ts));font-weight:600;color:var(--faint)}
  .cmp-nm{font-size:calc(16px*var(--ts));font-weight:600;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .cmp-id{font-size:calc(11px*var(--ts));color:var(--faint);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .cmp-rk{display:flex;align-items:center;gap:7px;margin-top:10px;font-size:calc(18px*var(--ts));line-height:1.1}
  .cmp-rk.q{font-family:inherit;text-transform:none;font-weight:600;font-size:calc(14px*var(--ts));color:var(--faint)!important}
  .cmp-crest{width:22px;height:22px}
  .cmp-lp{font-size:calc(12px*var(--ts));font-weight:600;color:var(--muted)}
  .cmp-vs{width:38px;height:38px;margin-top:30px;display:grid;place-items:center;transform:rotate(45deg);
    background:var(--panel2);border:1px solid color-mix(in srgb,var(--gold) 55%,var(--line));
    box-shadow:0 0 0 3px var(--bg),0 0 0 4px var(--line)}
  .cmp-vs span{transform:rotate(-45deg);font-weight:700;font-size:calc(11px*var(--ts));letter-spacing:.06em;color:var(--gold-txt)}
  .cmp-rows{border-top:1px solid var(--line)}
  .cmp-row{display:grid;grid-template-columns:minmax(0,1fr) 92px minmax(0,1fr);gap:10px;align-items:center;
    padding:9px 0;border-bottom:1px solid var(--line);font-size:calc(13px*var(--ts));font-variant-numeric:tabular-nums;color:var(--text)}
  .cmp-row:last-child{border-bottom:0}
  .cmp-row>:first-child{text-align:right}
  .cmp-row .k{text-align:center;font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--faint)}
  .cmp-row .win{color:var(--gold-txt);font-weight:600}
  /* ---- a shared snapshot: the page a link opens ----
     Somebody else's screen, so it is made to be looked at rather than worked in:
     whose and when at the top, the best rank as the headline, then the accounts
     as cards with their art properly visible — a banner, not the faint glow the
     app keeps behind a card you read all day. */
  body.snap-mode .wrap{display:none}
  .snap{max-width:1180px;margin:0 auto;padding:22px clamp(16px,4vw,40px) 56px}
  .sn-wait{padding:30vh 0;text-align:center;color:var(--faint);font-size:calc(13px*var(--ts))}
  .sn-top{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-bottom:clamp(30px,7vw,64px)}
  .sn-brand{display:flex;align-items:center;gap:13px;color:var(--text);text-decoration:none}
  .sn-brand .brand-mark{width:30px;height:30px;margin:4px}
  .sn-brand .brand-mark::before{width:12px;height:12px}
  .sn-brand .hex-display{font-size:calc(15px*var(--ts));font-weight:600}
  .sn-pill{display:inline-flex;align-items:center;gap:7px;padding:5px 11px;border-radius:var(--r-xs);
    font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.06em;color:var(--teal-txt);
    background:color-mix(in srgb,var(--teal) 9%,transparent);border:1px solid color-mix(in srgb,var(--teal) 30%,transparent)}
  .sn-pill .ico{width:13px;height:13px}
  .sn-hero{text-align:center}
  .sn-eyebrow{font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--gold-txt)}
  .sn-title{margin:12px auto 10px;max-width:22ch;font-size:calc(clamp(30px,5.4vw,48px)*var(--ts));font-weight:600;letter-spacing:-.025em;
    line-height:1.08;color:#fff;overflow-wrap:anywhere}
  .sn-sub{font-size:calc(14px*var(--ts));color:var(--muted)}
  .sn-sum{max-width:880px;margin:30px auto 0;text-align:left}
  .sn-sum .hx-in{display:grid;grid-template-columns:minmax(0,1.7fr) repeat(3,minmax(0,1fr))}
  .sn-sum .cell{padding:18px 20px;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:3px;border-left:1px solid #ffffff12}
  .sn-sum .cell:first-child{border-left:0;flex-direction:row;align-items:center;justify-content:flex-start;gap:16px}
  .sn-sum .k{font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
  .sn-sum b{display:block;font-size:calc(22px*var(--ts));font-weight:600;letter-spacing:-.01em;color:var(--text);
    font-variant-numeric:tabular-nums;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .sn-sum em{font-style:normal;font-size:calc(12px*var(--ts));color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .sn-sum .cell:first-child>div{min-width:0;display:flex;flex-direction:column;gap:3px}
  .sn-best-c{width:54px;height:54px;flex-shrink:0;filter:drop-shadow(0 4px 14px #0009)}
  .sn-dist{display:flex;gap:2px;height:5px;max-width:880px;margin:16px auto 0;border-radius:2px;overflow:hidden}
  .sn-dist i{display:block;min-width:4px}
  .sn-legend{display:flex;justify-content:center;flex-wrap:wrap;gap:6px 16px;margin-top:10px;font-size:calc(11px*var(--ts));color:var(--muted)}
  .sn-legend span{display:inline-flex;align-items:center;gap:6px}
  .sn-legend span::before{content:"";width:7px;height:7px;border-radius:1px;transform:rotate(45deg);background:var(--c)}
  .sn-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(300px,100%),1fr));gap:18px;margin-top:clamp(34px,6vw,52px)}
  .sn-card{animation:cardIn .5s cubic-bezier(.22,1,.36,1) both;animation-delay:calc(var(--i) * 45ms)}
  .sn-c{position:relative;overflow:hidden;display:flex;flex-direction:column;gap:12px;padding:0 18px 18px}
  .sn-art{position:relative;height:118px;margin:0 -18px;overflow:hidden;
    background:radial-gradient(120% 140% at 50% 0%,color-mix(in srgb,var(--tier) 26%,transparent),transparent 70%)}
  .sn-art img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 22%;opacity:.62}
  .sn-art::after{content:"";position:absolute;inset:0;
    background:linear-gradient(180deg,transparent 30%,color-mix(in srgb,var(--panel) 88%,transparent) 88%,var(--panel)),
      linear-gradient(90deg,color-mix(in srgb,var(--tier) 16%,transparent),transparent 60%)}
  .sn-card.dead .sn-art img{filter:grayscale(1);opacity:.25}
  .sn-head{position:relative;z-index:1;display:flex;align-items:flex-end;gap:12px;margin-top:-44px}
  .sn-crest{width:62px;height:62px;flex-shrink:0;display:grid;place-items:center;border-radius:50%;
    background:radial-gradient(circle at 50% 35%,#18222e,#05080d);
    box-shadow:0 0 0 1px color-mix(in srgb,var(--tier) 55%,transparent),0 0 0 4px var(--panel),0 8px 24px -6px color-mix(in srgb,var(--tier) 70%,transparent)}
  .sn-crest-i{width:40px;height:40px}
  .sn-crest b{font-size:calc(22px*var(--ts));font-weight:600;color:var(--faint)}
  .sn-id{flex:1;min-width:0;padding-bottom:4px}
  .sn-nm{display:flex;align-items:center;gap:6px;font-size:calc(17px*var(--ts));font-weight:600;color:#fff;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .sn-fav .ico{width:14px;height:14px;color:var(--gold);fill:var(--gold)}
  .sn-rid{font-size:calc(12px*var(--ts));color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .sn-head .regiontag{margin-bottom:6px}
  .sn-rank{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 10px}
  .sn-rank b{font-size:calc(22px*var(--ts));font-weight:600;letter-spacing:-.01em;color:var(--tier)}
  .sn-rank span{font-size:calc(14px*var(--ts));font-weight:500;color:var(--muted);font-variant-numeric:tabular-nums}
  .sn-rank .stchip{font-size:calc(11px*var(--ts));padding:2px 8px;border-radius:var(--r-xs);font-weight:600}
  .sn-wl{display:flex;justify-content:space-between;gap:10px;font-size:calc(12px*var(--ts));color:var(--muted);font-variant-numeric:tabular-nums}
  .sn-wl b{font-weight:600;color:var(--text)}
  .sn-wl b.wr-good,.sn-sum b.wr-good{color:var(--good)}
  .sn-spk{height:44px;margin:2px 0}
  .sn-meta{display:flex;flex-wrap:wrap;gap:6px 18px;font-size:calc(12px*var(--ts));color:var(--text)}
  .sn-meta i{font-style:normal;font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--faint);margin-right:7px}
  .sn-notes{margin-top:2px}
  .sn-notes .sk{color:var(--star);border-color:color-mix(in srgb,var(--gold) 42%,transparent);background:color-mix(in srgb,var(--gold) 12%,transparent)}
  .sn-empty{text-align:center;color:var(--muted);margin-top:40px}
  .sn-foot{margin-top:clamp(44px,8vw,72px);text-align:center;font-size:calc(12px*var(--ts));color:var(--faint);line-height:1.7}
  .sn-foot p{margin:0 auto 16px;max-width:52ch}
  .sn-foot .sn-riot{margin:28px auto 0;font-size:calc(11px*var(--ts));max-width:68ch}
  .sn-foot .btn,.sn-err .btn{text-decoration:none;padding:10px 18px;font-weight:600}
  .sn-err{max-width:480px;margin:16vh auto 0;text-align:center}
  .sn-err .hx-in{padding:34px 28px 30px}
  .sn-err .brand-mark{margin:4px auto 22px}
  .sn-err h1{font-size:calc(20px*var(--ts));font-weight:600;margin:0 0 8px}
  .sn-err p{font-size:calc(13px*var(--ts));color:var(--muted);line-height:1.6;margin:0 0 20px}
  @media (max-width:760px){
    .sn-sum .hx-in{grid-template-columns:repeat(3,minmax(0,1fr))}
    .sn-sum .cell{padding:14px 12px}
    .sn-sum .cell:first-child{grid-column:1/-1;justify-content:center;border-bottom:1px solid #ffffff12}
    .sn-sum .cell:nth-child(2){border-left:0}
    .sn-sum b{font-size:calc(18px*var(--ts))}
  }
  @media (prefers-reduced-motion:reduce){.sn-card{animation:none}}
  /* ---- the share window ---- */
  .sh-intro{font-size:calc(13px*var(--ts));color:var(--muted);line-height:1.6;margin:0 0 18px}
  .sh-intro b{color:var(--text);font-weight:600}
  .sh-body{display:flex;flex-direction:column;gap:18px}
  .sh-which{display:flex;width:100%}
  .sh-which button{flex:1;width:auto;height:38px;gap:6px;font-size:calc(13px*var(--ts));font-weight:600;color:var(--muted)}
  .sh-which button b{font-weight:600;font-size:calc(11px*var(--ts));color:var(--faint);font-variant-numeric:tabular-nums}
  .sh-which button.on b{color:inherit}
  .sh-opts{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(230px,100%),1fr));gap:10px}
  .sh-opt{display:flex;align-items:flex-start;gap:10px;padding:11px 13px;border-radius:var(--r-sm);cursor:pointer;
    border:1px solid var(--edge);background:#ffffff05;transition:border-color .16s ease,background .16s ease}
  .sh-opt:hover{border-color:var(--line2);background:#ffffff0a}
  .sh-opt:has(input:checked){border-color:color-mix(in srgb,var(--teal) 40%,transparent);background:color-mix(in srgb,var(--teal) 6%,transparent)}
  .sh-opt input{margin-top:2px;flex-shrink:0}
  .sh-opt span{display:flex;flex-direction:column;gap:2px;min-width:0}
  .sh-opt b{font-size:calc(13px*var(--ts));font-weight:600;color:var(--text)}
  .sh-opt em{font-style:normal;font-size:calc(12px*var(--ts));color:var(--faint);line-height:1.45}
  #shSize{font-variant-numeric:tabular-nums}
  /* ---- card art picker ----
     The League client's own skin selector is the shape people already know: the
     splash large up top, the choices as tall portraits underneath. The champion
     list before it is the client's square icons, searchable, since nobody scrolls
     a hundred and seventy of anything. */
  #artPicker .mdl{max-width:780px}
  .ap-top{flex-shrink:0;border-bottom:1px solid var(--line)}
  .ap-hero{position:relative;height:clamp(150px,30vw,236px);overflow:hidden;background:
    radial-gradient(120% 90% at 70% 0%,color-mix(in srgb,var(--gold) 14%,transparent),transparent 60%),
    linear-gradient(180deg,var(--panel2),var(--bg))}
  .ap-hero img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 22%;
    opacity:0;transform:scale(1.025);transition:opacity .35s ease,transform .6s cubic-bezier(.22,1,.36,1)}
  .ap-hero img.ld{opacity:1;transform:none}
  /* the stand-in while the splash downloads: the skin's own portrait, blurred to
     a wash of its colours and scaled so the blur never shows an edge */
  .ap-hero img.ap-ph{object-position:center 16%;filter:blur(16px) saturate(1.25) brightness(.8);
    transform:scale(1.18);transition:opacity .15s ease}
  .ap-hero img.ap-ph.ld{opacity:1;transform:scale(1.18)}
  .ap-hero::after{content:"";position:absolute;inset:0;pointer-events:none;
    background:linear-gradient(180deg,transparent 38%,color-mix(in srgb,var(--bg) 80%,transparent) 82%,var(--bg)),linear-gradient(90deg,color-mix(in srgb,var(--bg) 60%,transparent),transparent 55%)}
  .ap-cap{position:absolute;left:22px;right:22px;bottom:15px;z-index:1;min-width:0}
  .ap-cap b{display:block;font-size:calc(22px*var(--ts));font-weight:600;letter-spacing:-.01em;color:#fff;
    text-shadow:0 2px 12px #000c;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .ap-cap span{display:block;margin-top:3px;font-size:calc(11px*var(--ts));font-weight:600;letter-spacing:.14em;
    text-transform:uppercase;color:var(--gold-txt)}
  .ap-badge{position:absolute;top:13px;left:16px;z-index:1;display:inline-flex;align-items:center;gap:6px;
    padding:4px 9px;border-radius:var(--r-xs);font-size:calc(11px*var(--ts));font-weight:600;color:var(--text);
    background:color-mix(in srgb,var(--bg) 70%,transparent);border:1px solid color-mix(in srgb,var(--gold) 45%,transparent);
    backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
  .ap-badge .ico{width:12px;height:12px;color:var(--gold-txt)}
  .ap-empty{position:absolute;inset:0;display:grid;place-items:center;text-align:center;color:var(--faint);
    font-size:calc(13px*var(--ts))}
  .ap-empty .ico{display:block;width:30px;height:30px;margin:0 auto 8px;color:var(--gold-dim)}
  /* Strength: a strip of card with the art at the chosen strength behind a name
     and a rank, because the full splash above says nothing about how it will
     sit behind text */
  .ap-str{display:grid;grid-template-columns:minmax(0,230px) minmax(0,1fr);gap:18px;align-items:center;
    padding:12px 22px;border-top:1px solid var(--line)}
  .ap-prev{position:relative;height:76px;border-radius:var(--r-sm);overflow:hidden;isolation:isolate;
    background:var(--panel);box-shadow:0 0 0 1px var(--line)}
  /* the card's own art box, 230px tall, with only its top showing: the fade is
     drawn over that height on a card, so squeezing it into the strip's 76px made
     every strength look weaker here than it is on the card */
  .ap-prev img{position:absolute;left:0;right:0;top:0;width:100%;height:230px;z-index:-1;object-fit:cover;object-position:center 18%;
    -webkit-mask-image:linear-gradient(180deg,#000,#0008 45%,transparent 92%);mask-image:linear-gradient(180deg,#000,#0008 45%,transparent 92%)}
  .ap-prev-t{position:absolute;left:12px;right:12px;top:11px;display:flex;flex-direction:column;gap:3px;min-width:0}
  .ap-prev-t b{font-size:calc(14px*var(--ts));font-weight:600;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .ap-prev-t span{font-size:calc(13px*var(--ts));font-weight:600}
  .ap-ctl{display:flex;flex-direction:column;gap:7px;min-width:0}
  .ap-ctl .lbl{display:flex;justify-content:space-between;gap:10px;font-size:calc(12px*var(--ts));color:var(--muted)}
  .ap-ctl output{font-variant-numeric:tabular-nums;font-weight:600;color:var(--text)}
  .ap-ctl input[type=range]{width:100%;height:22px;padding:0;margin:0;border:0;background:none;box-shadow:none;accent-color:var(--gold)}
  .ap-opts{display:flex;align-items:center;gap:6px 14px;flex-wrap:wrap}
  .ap-all{display:inline-flex;align-items:center;gap:8px;font-size:calc(13px*var(--ts));color:var(--text);cursor:pointer}
  .ap-opts .hint{font-size:calc(12px*var(--ts))}
  /* phones: the strip beside the slider rather than over it, and a shorter
     splash, so the skins still get a full row under everything fixed above them */
  @media (max-width:620px){
    .ap-str{grid-template-columns:minmax(0,118px) minmax(0,1fr);gap:12px;padding:10px 16px}
    .ap-prev{height:60px}.ap-prev-t{left:9px;right:9px;top:8px;gap:2px}
    .ap-prev-t b{font-size:calc(12px*var(--ts))}.ap-prev-t span{font-size:calc(11px*var(--ts))}
    .ap-ctl{gap:4px}.ap-opts{gap:2px 10px}
  }
  .ap-off{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 22px;font-size:calc(12px*var(--ts));
    color:var(--muted);background:color-mix(in srgb,var(--gold) 8%,transparent);border-top:1px solid var(--line)}
  .ap-off button{padding:5px 10px;font-size:calc(12px*var(--ts))}
  .ap-bar{display:flex;align-items:center;gap:10px;padding:12px 22px}
  .ap-bar input{flex:1;min-width:0}
  .ap-bar .ap-t{font-size:calc(14px*var(--ts));font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .ap-bar .ap-n{margin-left:auto;font-size:calc(12px*var(--ts));color:var(--faint);white-space:nowrap}
  .ap-back{padding:6px 11px 6px 8px}
  .ap-back .ico{width:15px;height:15px;transform:rotate(90deg)}
  .ap-grid{display:grid;gap:10px;padding:16px 22px 20px;align-content:start}
  .ap-grid.ch{grid-template-columns:repeat(auto-fill,minmax(74px,1fr));gap:12px 8px}
  .ap-grid.sk{grid-template-columns:repeat(auto-fill,minmax(124px,1fr));gap:12px}
  .ap-ch,.ap-sk{all:unset;box-sizing:border-box;cursor:pointer;position:relative;min-width:0}
  .ap-ch{display:flex;flex-direction:column;align-items:center;gap:6px;padding:4px 2px;border-radius:var(--r-sm)}
  .ap-ch i{display:block;width:54px;height:54px;border-radius:var(--r-sm);overflow:hidden;
    background:var(--panel3);box-shadow:0 0 0 1px var(--line2);transition:box-shadow .16s ease,transform .16s ease}
  .ap-ch img{display:block;width:100%;height:100%;transform:scale(1.1)}
  .ap-ch span{max-width:100%;font-size:calc(11px*var(--ts));color:var(--muted);text-align:center;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .ap-ch:hover i{box-shadow:0 0 0 1px var(--gold-mid);transform:translateY(-2px)}
  .ap-ch:hover span{color:var(--text)}
  .ap-ch.on i{box-shadow:0 0 0 2px var(--gold),0 0 16px -4px var(--gold)}
  .ap-ch.on span{color:var(--gold-txt);font-weight:600}
  .ap-sk{display:block;aspect-ratio:3/4;border-radius:var(--r-md);overflow:hidden;background:var(--panel3);
    box-shadow:0 0 0 1px var(--line2),var(--sh-2);transition:box-shadow .18s ease,transform .18s cubic-bezier(.22,1,.36,1)}
  .ap-sk img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 12%}
  .ap-sk::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,transparent 52%,color-mix(in srgb,var(--bg) 90%,transparent))}
  .ap-sk .nm{position:absolute;left:9px;right:9px;bottom:8px;z-index:1;font-size:calc(12px*var(--ts));font-weight:600;
    line-height:1.25;color:var(--text);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
  .ap-sk .cur{position:absolute;top:7px;left:7px;z-index:1;padding:2px 6px;border-radius:var(--r-xs);
    font-size:calc(10px*var(--ts));font-weight:600;letter-spacing:.04em;color:var(--teal-ink);background:var(--teal)}
  .ap-sk:hover{transform:translateY(-3px);box-shadow:0 0 0 1px var(--gold-mid),var(--sh-3)}
  .ap-sk.on{box-shadow:0 0 0 2px var(--gold),0 0 22px -6px var(--gold),var(--sh-3)}
  .ap-sk.on::before{content:"";position:absolute;top:7px;right:7px;z-index:2;width:20px;height:20px;border-radius:50%;
    background:var(--gold) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23010a13' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E") center/12px no-repeat}
  .ap-sk.dead img{display:none}
  .ap-ch:focus-visible,.ap-sk:focus-visible{outline:2px solid var(--teal-txt);outline-offset:3px}
  .ap-sk.skel,.ap-ch.skel i{cursor:default;background:linear-gradient(100deg,var(--panel2) 30%,var(--panel3) 50%,var(--panel2) 70%) 0 0/300% 100%;
    animation:apShim 1.3s linear infinite}
  .ap-ch.skel span{width:70%;height:9px;border-radius:2px;background:var(--panel2)}
  @keyframes apShim{to{background-position:-150% 0}}
  .ap-msg{grid-column:1/-1;padding:26px 6px;text-align:center;color:var(--muted);font-size:calc(13px*var(--ts));line-height:1.55}
  .ap-msg button{margin-top:10px}
  @media (max-width:620px){
    .ap-cap,.ap-bar,.ap-off{padding-left:16px;padding-right:16px}.ap-cap{left:16px;right:16px}
    .ap-hero{height:clamp(118px,32vw,150px)}.ap-cap{bottom:12px}.ap-cap b{font-size:calc(19px*var(--ts))}
    .ap-grid{padding:14px 16px 18px}
    .ap-grid.sk{grid-template-columns:repeat(auto-fill,minmax(102px,1fr));gap:10px}
  }
  .toast-log{max-width:420px;font-size:calc(12px*var(--ts));line-height:1.45}

  .panel-close{transition:color .16s,background .16s,transform .2s cubic-bezier(.22,1,.36,1)}
  .panel-close:hover{transform:rotate(90deg)}
  .chip{transition:background .15s ease,border-color .15s ease,color .15s ease,transform .14s cubic-bezier(.22,1,.36,1)}
  .chip:active{transform:scale(.95)}
  .c-portrait{transition:transform .24s cubic-bezier(.22,1,.36,1)}
  .card:hover .c-portrait{transform:scale(1.05)}
  .regiontag{transition:background .2s,border-color .2s}
  .card:hover .regiontag{color:var(--text);border-color:var(--line2)}

  /* ---- motion ---- */
  /* The lit border used to be a `background` transition between two gradients,
     and gradients do not interpolate — the browser holds the old one and swaps
     at the end. Going in that reads as a delay; coming out it reads as a snap,
     which is the "glow going back" that looked wrong. The bright edge is its own
     layer now and only its opacity moves, which interpolates exactly, so the
     lift and the light travel together in both directions.
     Slightly longer on the way out than in: matching the entry timing makes a
     hover-out feel abrupt, because nothing initiated it. */
  .grid>.hx{position:relative;
    transition:transform .34s cubic-bezier(.22,1,.36,1),box-shadow .34s ease}
  /* z-index 0, not -1: a negative one would sit behind the card's own background
     and never be seen. It has to land between that background and .hx-in. */
  .grid>.hx::after{content:"";position:absolute;inset:0;border-radius:inherit;z-index:0;
    pointer-events:none;opacity:0;transition:opacity .34s ease;
    background:linear-gradient(155deg,#ffffff3d,#ffffff12 38%,#ffffff06)}
  /* Lift and glow only under a real pointer: on a touch screen the glow fading in
     is "something appeared", which Safari answers by swallowing the tap that
     caused it — and a lifted card that stays lifted after a tap says nothing. */
  @media (hover:hover){
    .grid>.hx:hover{transform:translateY(-3px);box-shadow:var(--sh-3);transition-duration:.22s}
    .grid>.hx:hover::after{opacity:1;transition-duration:.22s}
    body[data-fx="subtle"] .grid>.hx:hover::after{opacity:.4}
  }
  /* the inner pane has to paint over the glow layer, not under it */
  .hx-in{position:relative;z-index:1}
  /* ---- Effects (Settings → Appearance) ----
     Subtle: nothing moves under the pointer, nothing loops, the glows are quieter.
     Off: nothing animates or glows at all. Reduce-motion is handled on its own
     below, whichever is picked. */
  body:is([data-fx="subtle"],[data-fx="off"]) :is(.grid>.hx,.grid.wall>.hx,.to-top,.star,.panel-close):hover,
  body:is([data-fx="subtle"],[data-fx="off"]) :is(.acts button,.a-login):hover .ico{transform:none}
  body:is([data-fx="subtle"],[data-fx="off"]) :is(.grid.boot>.hx){animation:none}
  body:is([data-fx="subtle"],[data-fx="off"]) .confetti{display:none}
  body[data-fx="off"] *,body[data-fx="off"] *::before,body[data-fx="off"] *::after{animation:none!important;transition:none!important}
  body[data-fx="off"] .grid>.hx::after{display:none}
  /* Nothing here loops on its own except the header mark, the spinner and the
     sweep over a card being checked. A hidden tab stops being painted, but the
     animations are still live objects the compositor keeps ticking, and this app
     is meant to sit open in a background tab for days. Parked explicitly instead.
     The crests deliberately have no loop of their own: one animation per card is
     sixty of them on a full vault, for a glow nobody is looking at. */
  body.away .brand-mark::before,body.away .spin,
  body.away .hx-in.scanning::after{animation-play-state:paused}
  /* A check run had a progress bar and sixty perfectly still cards. The run knows
     which account it is on at every moment and the card already carries that state
     — it just never showed it. A sweep travels the card being read, so the work is
     visible where the work is happening.
     Lives on .hx-in, whose ::after is free; .hx's own is the hover glow. All three
     layouts already clip their overflow. */
  @keyframes scanSweep{from{transform:translateX(-110%)}to{transform:translateX(110%)}}
  .hx-in.scanning::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:2;
    background:linear-gradient(102deg,transparent 38%,
      color-mix(in srgb,var(--teal) 22%,transparent) 50%,transparent 62%);
    animation:scanSweep 1.25s cubic-bezier(.5,0,.5,1) infinite}
  @keyframes cardIn{from{opacity:0;transform:translateY(12px) scale(.985)}to{opacity:1;transform:none}}
  .grid.boot>.hx{animation:cardIn .5s cubic-bezier(.22,1,.36,1) both;animation-delay:var(--d,0ms)}
  /* The line used to be revealed with stroke-dasharray, which is the wrong tool
     here: vector-effect:non-scaling-stroke makes the dash pattern measure in
     screen pixels, while pathLength normalises the path to 100 units. The two
     never agreed, so "100 on, 100 off" left a real gap in any chart whose drawn
     length passed 100 px — the stretch to the newest point simply never showed.
     A wipe has no such coupling. No fill-mode on purpose: once it has run the
     clip is gone entirely, so it can't clip a tooltip later. */
  @keyframes drawIn{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
  .grid.boot .lpc,.hx.flash .lpc{animation:drawIn .8s cubic-bezier(.22,1,.36,1)}
  @keyframes borderPulse{0%{background:var(--teal)}100%{background:linear-gradient(155deg,#ffffff1f,#ffffff0a 38%,#ffffff05)}}
  .hx.flash{animation:borderPulse 1.1s ease-out}
  @keyframes toastIn{from{opacity:0;transform:translate(-50%,14px) scale(.96)}to{opacity:1;transform:translate(-50%,0) scale(1)}}
  #toast{animation:toastIn .28s cubic-bezier(.22,1,.36,1)}
  .confetti{position:fixed;pointer-events:none;z-index:60}
  .confetti i{position:absolute;left:50%;top:40%;width:6px;height:10px;border-radius:2px;animation:pop 1.1s ease-out forwards}
  @keyframes pop{from{transform:translate(0,0) rotate(0)}to{transform:translate(var(--tx),var(--ty)) rotate(var(--rz));opacity:0}}
  @media (prefers-reduced-motion:reduce){
    .spin,.brand-mark::before,.grid.boot>.hx,.grid.boot .lpc,.hx.flash .lpc,.hx.flash,#toast,.confetti i,
    #bulkAdd:not(.hidden),#lock:not(.hidden),
    #moreMenu:not(.hidden),#bulkbar:not(.hidden),#previewBanner:not(.hidden),.banner.show,
    .login.reveal,.acts.reveal,.c-notes.reveal,.login.reveal .lrow,.star.reveal,.hx.dash-in,.sec-b,
    .modal::before,.modal:not(.hidden) .mdl,.acc-prev.show{animation:none!important}
    .hx-in.scanning::after{animation:none!important;content:none}
    .grid>.hx{transition:none}
    .modal,.modal .mdl,.card-menu,#moreMenu,#toast{transition:none!important}
    .modal.hidden,.modal.hidden .mdl,.card-menu.hidden,#moreMenu.hidden{animation:none!important}
    .panel-close:hover,.chip:active,.card:hover .c-portrait,.a-login:hover .ico,.acts button:hover .ico,
    .c-drawer:hover .ico{transform:none}
    /* the flipped chevrons stay — they say open vs closed, they aren't decoration */
    .c-drawer .ico,.sec-h .ico{transition:none}
    html{scroll-behavior:auto}
  }
  @media (pointer:coarse){
    .c-ref{width:36px;height:36px;margin-block:-11px}
    .acts button,.acts a,.lbtns button{font-size:calc(13px*var(--ts));padding:9px 13px}
    .star{font-size:calc(20px*var(--ts));padding:2px 6px 2px 0}
    button,.btn{padding:10px 15px}
    .seg button{width:40px;height:36px;padding:0} /* keep fixed; don't inherit the fat button padding */
    /* iOS zooms the page into any field typed in at under 16px. Text size may
       scale the rest of the type down, never a field below that, and the
       smaller fields with rules of their own (the note editor, the compact
       selects) are held to it too. */
    :is(input:not([type=checkbox]):not([type=radio]):not([type=color]),select,textarea){font-size:max(16px,calc(16px*var(--ts)))!important}
    /* A thumb against controls sized for a mouse: the selection boxes were 13 and
       15px, the chart's D/W/M 17px tall, the Riot ID's copy line 16px, the header
       bar's segments 16px. Each is at least WCAG's 24px now — on touch screens
       only; with a mouse nothing changes size. */
    .bulkchk,#tSelectAll{width:24px;height:24px}
    .lpc-range button{padding:7px 9px}
    .rid{padding-block:5px;margin-block:-3px}
    .rib-bar{height:24px}
    .cmd-count .warn{padding:6px 10px}
    .c-notes.blank>button,.c-notes.blank .nf-link{min-height:30px} /* matches the all:unset rule it has to beat */
  }
  @media (max-width:760px){
    .top{align-items:flex-start;gap:16px}
    .tools{padding:10px}
    .cmd{padding:10px}
    /* The count took a row of its own and the layout switch another below it.
       Commands stretch to fill the rows they land on; the readout and the switch
       share the last row, the count wrapping its halves rather than the switch
       falling to a row of its own. */
    .cmd>button{flex:1 1 auto}
    .cmd-r{margin-left:0;flex:1 1 100%}
    .cmd-count{flex:1 1 0;min-width:0;flex-wrap:wrap;row-gap:2px}
    /* wrapped onto four or five rows this is a third of a phone screen; pinning
       that much of the viewport costs more than scrolling back up to it */
    .console{position:static}
  }
  /* On a phone the filters wrapped wherever they ran out of room — Sort alone on a
     row, Archived and the checkbox trailing — so every row was a different ragged
     length. They sit on a two-column grid. (The command row is set out above.) */
  @media (max-width:560px){
    .cmd>#bCheckAll{flex-basis:100%} /* the primary gets the full first row, not half of it */
    .seg button{width:36px}
    .tools{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;align-items:stretch}
    .tools>*{min-width:0;width:100%}
    .tools input[type=search]{grid-column:1/-1;min-width:0}
    .tools>label{justify-content:center}
  }
  @media (max-width:440px){.cmd{gap:8px}.cmd>button{padding-inline:10px}}
  /* A phone's header was four rows before anything else: the gem and the wordmark,
     "Summoner Vault" and its badge, the rank bar, then the tray alone on a row of
     its own — 230px. It is an app bar now: the wordmark on the left, the tray on
     the right, the rank bar under both. The gem and the strapline stand down;
     the wordmark is the brand at this size. */
  @media (max-width:560px){
    .top{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:12px 10px}
    .top>.brand{grid-column:1;grid-row:1;gap:0;min-width:0}
    .top>.brand .brand-mark,.top>.brand .brand-sub{display:none}
    .top>.bar{grid-column:2;grid-row:1;margin-left:0}
    .top>.ribbon{grid-column:1/-1;grid-row:2;max-width:none}
    .top h1{font-size:calc(20px*var(--ts));white-space:nowrap}
    #bDashToggle .dt-v{display:none}
    #bDashToggle .dt-n{text-transform:capitalize}
  }
  @media (max-width:400px){.top h1{font-size:calc(18px*var(--ts))}}
  /* On a phone the toolbar's filters were the whole first screen: search, two rows
     of dropdowns, Favorites, Archived and Select all, before a single account. Only
     search stays out; the rest fold behind Filters, whose badge counts what is on so
     a filter is never on unseen (the chips under the toolbar name the rest). */
  .t-filters{display:none}
  .t-filters .t-n{margin-left:2px;min-width:18px;height:18px;padding:0 5px;border-radius:var(--r-xs);display:inline-flex;align-items:center;
    justify-content:center;font-size:calc(11px*var(--ts));font-weight:700;color:var(--teal-ink);background:var(--teal)}
  .t-filters .t-n[hidden]{display:none}
  .t-select{display:none}
  @media (max-width:560px){
    .tools{display:flex;flex-wrap:wrap}
    /* search and Filters share the first row (search takes all but Filters' width,
       so the rest always starts a row of its own), the rest two to a row under it */
    .tools>input[type=search]{flex:1 1 calc(100% - 128px);width:auto;min-width:0}
    .tools>.t-filters{display:inline-flex;flex:0 0 auto;width:auto}
    .tools>:not(input[type=search]):not(.t-filters){flex:1 1 calc(50% - 4px)}
    /* !important: Select all is a label with an inline display of its own */
    .tools:not(.open)>:not(input[type=search]):not(.t-filters){display:none!important}
    /* One ranks button, not two stacked: with accounts out of date, Refresh N stale
       is the one a phone needs (the rest were read recently); with none, Check all. */
    .cmd:has(#bCheckStale:not(.hidden)) #bCheckAll{display:none}
    .tools.open>.t-filters{background-color:var(--panel3);border-color:var(--line2)}
    /* The figures wrapped two to a row, a third alone beneath them, and the champion
       chips wrapped into a block of their own: together most of a screen. Each is
       one row now that slides sideways, the next figure peeking in at the edge to
       say there is more. */
    /* On a phone the count and "N need a refresh" stack; the button's padding (its
       hit area) pushed its words in from the count's, so the second line started
       10px right of the first. The margin takes the padding back out. */
    .cmd-count{flex-direction:column;align-items:flex-start;gap:0}
    .cmd-count .warn{margin-left:-10px}
    #dash .dash,#dash .dash[style*="--cols"]{display:flex;overflow-x:auto;scroll-snap-type:x proximity;
      overscroll-behavior-x:contain;scrollbar-width:none}
    #dash .dash>.stat{flex:0 0 min(62%,220px);scroll-snap-align:start}
    /* the edge fades, so a figure cut by it reads as "more this way", not as all there is */
    #dash .dash{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 32px),transparent);mask-image:linear-gradient(90deg,#000 calc(100% - 32px),transparent)}
    #dash .dist{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;padding-right:20px}
    #dash .dist>*{flex-shrink:0}
    #dash .dash::-webkit-scrollbar,#dash .dist::-webkit-scrollbar{display:none}
  }
  @media (max-width:560px){.formgrid{grid-template-columns:1fr;padding:18px}.card{padding:15px}
    .c-drawer{margin:1px -15px -15px}
    /* a phone has ~150px a side: the tier word drops to its own size, the crest
       above it, so DIAMOND II reads on one line instead of breaking at the space */
    .cmp{padding:18px 14px 4px}
    .cmp-head{gap:8px}
    .cmp-rk{flex-direction:column;gap:4px;font-size:calc(15px*var(--ts))}
    .cmp-rk>span{white-space:nowrap}
    .cmp-vs{width:30px;height:30px;margin-top:24px}
    .cmp-row{grid-template-columns:minmax(0,1fr) 86px minmax(0,1fr);gap:8px;font-size:calc(12px*var(--ts))}
    .cmp-row .k{white-space:nowrap}}
