/* Emberbound-3D glue — everything this client draws that the ported 2D kit has
 * no rule for. Written against theme.css tokens so it stays one look. */

/* The world canvas is a 3D render, not pixel art: undo the 2D sheet's
 * nearest-neighbour hint or every antialiased edge shimmers. */
#view{image-rendering:auto;cursor:var(--cur-default,default)}

/* An item you could pick up wears the hand that would pick it up.
 *
 * `--cur-reach` and `--cur-grab` are the open and shut frames of the player's
 * OWN hand, cut out of the sprite strip at runtime by ui/cursor.ts — so the
 * hand that hovers a jacket, the hand that closes on it and the hand that
 * carries it across the bag are one hand, in one skin.
 *
 * Only slots with something in them: an empty socket has nothing to reach for.
 * A cursor cannot animate, so :active does the grab — the pointer going down
 * IS the closing, and on a bag tile the real sprite takes over from there. */
.eq2:not(.empty),.is2:not(.empty){cursor:var(--cur-reach,grab)}
.eq2:not(.empty):active,.is2:not(.empty):active{cursor:var(--cur-grab,grabbing)}
/* What the pointer becomes over something worth clicking is decided in
 * ui/cursor.ts and set inline: it depends on WHAT is under it — a monster puts
 * the weapon in your hand — which no stylesheet can know. */

/* ---------------------------------------------------------------- zone banner */
/* Which ring of the island you are standing in, Albion-style. Colour comes in
 * on the element's --zone custom property.
 *
 * This is the CROSSING, not the state. The banner used to be a permanent plate
 * pinned to the top centre, which is the most contested strip of the whole HUD:
 * the runic top bar and its day dial own y 6..73, the bars and arcs vitals
 * styles sit at 76/80, and the target frame at 96 — the plate landed on top of
 * all of them. Where you ARE now lives in the minimap's own column (.mmzone);
 * this element only announces the moment you pass a border, well clear of the
 * top strip, and fades itself out. Nothing else may be placed here: it is over
 * the world, not over the HUD. */
/* 20% of the viewport, but never higher than 206px: on a 720-tall window a bare
   percentage lands at 158 and puts the flash straight back on the arcs vitals
   (80..200) and the target frame. The floor clears both, and still stops short
   of the level-up flourish at 38%. */
#zonebanner{position:absolute;top:max(20%, 206px);left:50%;transform:translateX(-50%);
  z-index:70;pointer-events:none;opacity:0;text-align:center;
  display:flex;flex-direction:column;align-items:center;gap:3px}
#zonebanner i{font-style:normal;font-size:10px;letter-spacing:.34em;
  text-transform:uppercase;color:var(--dim);text-shadow:0 1px 3px #000}
#zonebanner b{font-size:30px;font-weight:800;letter-spacing:.16em;
  text-transform:uppercase;color:var(--zone,#7fd07f);
  text-shadow:0 2px 6px #000,0 0 18px var(--zone,#7fd07f)}
/* one pass: rise and brighten, hold, fade. `.on` is added for the length of the
   animation and taken off again, so a second crossing can replay it. */
#zonebanner.on{animation:zoneCross 2.6s ease-out forwards}
@keyframes zoneCross{
  0%{opacity:0;transform:translateX(-50%) translateY(10px)}
  14%{opacity:1;transform:translateX(-50%) translateY(0)}
  70%{opacity:1;transform:translateX(-50%) translateY(0)}
  100%{opacity:0;transform:translateX(-50%) translateY(-8px)}}
@media (prefers-reduced-motion:reduce){
  #zonebanner.on{animation:zoneCrossFade 2.6s ease-out forwards}
  @keyframes zoneCrossFade{0%,100%{opacity:0}14%,70%{opacity:1}}}

/* ---------------------------------------------------------------- target frame */
/* The marked creature, centred under the top bar. */
#targetframe{position:absolute;top:96px;left:50%;transform:translateX(-50%);
  z-index:6;min-width:220px;padding:8px 12px 9px}
#targetframe .tf-name{display:flex;align-items:baseline;gap:8px;margin-bottom:5px;
  padding-right:16px}
/* Drop the lock. Escape has always done it; this is the same verb for a hand
   that is on the mouse, sitting in the corner of the frame it dismisses. */
#targetframe .tf-drop{position:absolute;top:3px;right:5px;width:20px;height:20px;
  padding:0;line-height:14px;text-align:center;font-size:11px;cursor:pointer;
  color:var(--faint);background:none;border:1px solid transparent;border-radius:4px}
#targetframe .tf-drop:hover{color:#ffd9d0;background:#3a1a20;border-color:#7a3340}
#targetframe .tf-name b{font-size:13px}
#targetframe .tf-name span{font-size:10px;color:var(--dim);letter-spacing:.08em;
  text-transform:uppercase}
#targetframe .bar{height:12px;border-radius:6px}
#targetframe.hostile .tf-name b{color:#f0a89a}
#targetframe.friendly .tf-name b{color:#9ac4ea}
/* A creature's name in the frame carries the same danger colour it wears in
   the world, and overrides the plain hostile tone above. Same five bands, same
   hues as .nameplate.con-* — lifted a little, because this text sits on the
   frame's own dark panel rather than on grass, and can afford it. */
#targetframe.con-easy .tf-name b{color:#7db4ee}
#targetframe.con-normal .tf-name b{color:#f0ede6}
#targetframe.con-tough .tf-name b{color:#f5ad50}
#targetframe.con-hard .tf-name b{color:#f56552}
#targetframe.con-deadly .tf-name b{color:#d63a3a;text-shadow:0 0 12px #8b000088}
/* Enmity. Who this creature is actually angry at, and how close you are to
   being it — the FFXIV enmity list, minus the party frame it usually sits in.
   The row is absent, not empty, for anything with no threat table: "this boar
   is not fighting anybody" and "you are second on the list" must not look
   alike. */
#targetframe .tf-enmity{display:flex;align-items:center;gap:8px;margin-top:5px}
#targetframe .tf-enmity .who{font-size:10px;color:var(--dim);letter-spacing:.06em;
  text-transform:uppercase;white-space:nowrap;flex:0 0 auto}
#targetframe .tf-enmity .bar.th{height:4px;border-radius:2px;flex:1 1 auto;min-width:60px}
#targetframe .tf-enmity .bar.th i{background:linear-gradient(90deg,#8a6a3a,#e0a94a)}
/* Holding it is the thing worth seeing from the corner of an eye. */
#targetframe.aggro{box-shadow:0 0 0 1px #e0a94a66,0 0 18px -6px #e0a94a}
#targetframe.aggro .tf-enmity .who{color:#e0a94a}
#targetframe.aggro .tf-enmity .bar.th i{background:linear-gradient(90deg,#e0a94a,#ffd98a)}

/* ---------------------------------------------------------------- labels layer */
#labels{position:fixed;inset:0;pointer-events:none;overflow:hidden;z-index:5}
.nameplate{position:absolute;transform:translate(-50%,-100%);text-align:center;
  font-size:11px;white-space:nowrap;text-shadow:0 1px 3px #000,0 0 6px #000;
  will-change:transform;left:0;top:0}
.nameplate .hp{width:54px;height:3px;margin:2px auto 0;background:#000b;
  border-radius:2px;overflow:hidden}
.nameplate .hp span{display:block;height:100%;background:var(--hp)}
.nameplate.friendly{color:#8fc7f0}
.nameplate.hostile{color:#e08a7a}
.nameplate.neutral{color:#cfc8b8}
/* A murderer. Honour at or below RED_AT, derived server-side into STATUS_KARMA.
   Deliberately the loudest colour any name can take, and brighter than the
   creature red beside it: in a world where every zone is a fighting zone this
   is the only warning the interface gives about a PERSON, and it has to be
   read at a glance across a field. The glow survives the fog, where a player's
   name is all that is left of them. */
.nameplate.murderer{color:#ff3b30;text-shadow:0 1px 3px #000,0 0 8px #ff2a1fcc}
/* ------------------------------------------------------------ danger colours
   What a creature's name is worth saying: not its level — that number was
   `tier * 5` and it lied — but what it will do to YOU. Five bands, computed in
   shared/danger.ts against the reader's own level, and the same five wherever
   a creature is named, so the colour learned over a monster's head is the
   colour understood on the target frame.

   The ramp is chosen to survive a bright green meadow at dusk with only a
   black shadow behind it, and to be separable at the far end: `hard` and
   `deadly` are the pair a player must never confuse, so they differ in
   LIGHTNESS as well as hue, and `deadly` carries a red glow of its own rather
   than going darker still — a dark red on a dark world reads as unreadable
   text, not as menace. */
.nameplate.con-easy{color:#6ea8e8}
.nameplate.con-normal{color:#ece9e2}
.nameplate.con-tough{color:#f0a445}
.nameplate.con-hard{color:#ef5a48}
.nameplate.con-deadly{color:#c0272d;
  text-shadow:0 1px 3px #000,0 0 6px #000,0 0 10px #8b000099}
/* A player's name carrying through the fog. Only players ever get this — a
   creature in the dark has no plate at all — and it has to read as "somebody
   is out there" rather than as a plate that failed to draw: dimmed and letter
   spaced, with the glow doing the work the text no longer does. The health bar
   is hidden from the DOM side; this is only what is left. */
.nameplate.fogged{opacity:.62;letter-spacing:.06em;
  text-shadow:0 1px 3px #000,0 0 10px #0008}


/* ------------------------------------------------- the cave you are standing in */
/* A standing banner, not a line of chat. A player four rooms into a warren has
   to be able to answer "how hard is this, is there a boss, how long have I got"
   at any moment, and a sentence that scrolled past twenty minutes ago cannot.
   Top-centre and small: it is a reference, not an announcement, and the zone
   banner already owns the moment of arrival. */
/* TOP-LEFT, under the portrait frame, not top-centre: centred at top:8px it
   sat on the top bar's own plates and day dial on every screen (measured
   358x31 of overlap at 1080p), and the two centred spots under the bar are
   taken — the banner vitals at 46 and the target frame at 96. The gap under
   the portrait (which draws to 175 with its badge) is the one place in the
   top strip nothing else claims in any vitals style; the party frame, which
   would be next, steps down to make room while the bar is up. On a phone
   touch.css moves it under the vitals, which live in that corner there. */
#dungeonbar{position:fixed;top:184px;left:12px;
  display:flex;align-items:center;gap:9px;z-index:6;pointer-events:none;
  padding:5px 14px;border-radius:5px;
  background:linear-gradient(#0e0c0ae8,#0e0c0ab0);
  border:1px solid color-mix(in srgb,var(--c) 45%,transparent);
  border-top:2px solid var(--c);
  box-shadow:0 3px 14px #000a;
  font-size:12.5px;color:#e7dfd0;letter-spacing:.03em;
  text-shadow:0 1px 3px #000}
#dungeonbar .db-skulls{color:var(--c);letter-spacing:-.04em}
#dungeonbar .db-name{font-weight:600}
#dungeonbar .db-tier{color:var(--c);font-size:10.5px;text-transform:uppercase;
  letter-spacing:.08em}
#dungeonbar .db-boss{padding:1px 5px;border-radius:2px;background:#7d1f16;
  color:#ffd9c8;font-size:9.5px;letter-spacing:.08em}
#dungeonbar .db-clock{color:#9a917f;font-size:10.5px}
/* The last minute. The colour change is the whole warning -- a countdown that
   looks the same at ten minutes and at forty seconds is a countdown nobody
   reads until it is over. */
#dungeonbar.urgent{border-color:#c8452f;border-top-color:#c8452f;
  animation:dbpulse 1.1s ease-in-out infinite}
#dungeonbar.urgent .db-clock{color:#ff9c86}
@keyframes dbpulse{0%,100%{box-shadow:0 3px 14px #000a}
  50%{box-shadow:0 3px 14px #000a,0 0 18px #c8452f66}}
/* Sealed: the way in has fallen and you are committed to the one run. Read
   down rather than up -- the bar loses its second colour and goes quiet --
   because nothing is now counting and the cave is going nowhere while you are
   in it. It is a statement of fact, not the end of a warning. */
#dungeonbar.sealed{border-top-color:#4a423a}
#dungeonbar.sealed .db-clock{color:#7d7263;font-style:italic}

/* ------------------------------------------------- world tags: caves and loot */
/* Labels over things that are not people. A nameplate is a name and a health
   bar; none of these is either, which is why they are a separate shape rather
   than a variant. `--c` is set per tag from the item's rarity or the cave's
   tier, so one rule serves five colours and the palette lives in shared/loot.ts
   where the glow and the chest agree with it. */
.worldtag{position:absolute;left:0;top:0;will-change:transform}
.tag{text-align:center;white-space:nowrap;
  text-shadow:0 1px 3px #000,0 0 7px #000}

/* A thing on the floor, Diablo-style: the item IS its label, and the colour is
   the whole message. Boxed, because an unboxed word on a dark cave floor at
   eleven pixels is unreadable in exactly the places this is used. */
.tag.drop{font-size:11.5px;color:var(--c);background:#0b0a09cc;
  border:1px solid color-mix(in srgb,var(--c) 55%,transparent);
  border-radius:3px;padding:1px 6px;letter-spacing:.02em}

/* Coin. Deliberately not a rarity: a pile of silver is a pile of silver, and
   dressing it in a band's colour would put it in the same list as the gear
   when the whole point is that it is NOT in that list -- you walk through
   coin and you decide about gear. The disc is drawn rather than fetched: one
   gradient is smaller than any icon file and it never 404s. */
.tag.drop.coin{display:flex;align-items:center;gap:4px}

/* The landing. A drop's name is withheld while the item is in the air (see
   labels.ts) and pops in the frame it touches down -- a quick overshoot in,
   like a stamp, because the name IS the impact as far as the interface is
   concerned. The wrapper carries the projection transform, so the scale
   animates here on the shape and never fights the placement. */
.tag.drop.pop{animation:dropPop .28s cubic-bezier(.18,1.5,.4,1) backwards}
@keyframes dropPop{from{transform:scale(.35);opacity:0}}
/* The coin glyph takes the pile's own colour (--c, set from COIN_COLOR), so a
   diamond pile is not labelled with a silver disc. The white highlight and the
   dark rim stay fixed: they are what make it read as a struck disc rather than
   a dot, and tinting them too turns the small coins into mush. */
.tag-coin{width:8px;height:8px;border-radius:50%;flex:none;
  background:radial-gradient(circle at 35% 32%,#ffffff,var(--c,#cfc7b4) 58%,#00000066);
  box-shadow:0 0 4px color-mix(in srgb,var(--c,#d8d2c4) 50%,transparent)}

/* A chest. The name says what it is worth; the bar only exists while somebody
   is prising at it, and it is broadcast rather than private because "somebody
   is four seconds into the legendary chest" is a thing the whole room is
   acting on. */
.tag.chest{font-size:12px;color:var(--c);font-weight:600;letter-spacing:.03em}
.tag.chest .tag-bar{width:62px;height:4px;margin:3px auto 0;background:#000c;
  border:1px solid #0008;border-radius:2px;overflow:hidden}
.tag.chest .tag-bar span{display:block;height:100%;width:0;
  background:linear-gradient(90deg,color-mix(in srgb,var(--c) 60%,#000),var(--c));
  transition:width 120ms linear}

/* The plate over a cave mouth, and the most important label in the feature:
   it is the entire promise that a player decides whether to go in BEFORE the
   loading screen rather than after it. Three lines, in the order the decision
   is made -- which cave, how hard, how long is left. */
.tag.mouth{font-size:12px;color:#e7dfd0;line-height:1.35;
  background:linear-gradient(#0d0b09e6,#0d0b09b8);
  border:1px solid color-mix(in srgb,var(--c) 60%,transparent);
  border-top:2px solid var(--c);
  border-radius:4px;padding:4px 10px 3px;
  box-shadow:0 2px 10px #000a,0 0 14px color-mix(in srgb,var(--c) 22%,transparent)}
.tag.mouth .tag-name{font-weight:600;letter-spacing:.03em}
.tag.mouth .tag-tier{font-size:10.5px;color:var(--c);letter-spacing:.06em;
  text-transform:uppercase}
/* The skulls carry the reading at a distance the word cannot: the LENGTH of
   the row is legible from further away than any letterform. */
.tag.mouth .tag-skulls{letter-spacing:-.04em;margin-right:3px}
.tag.mouth .tag-boss{margin-left:6px;padding:0 4px;border-radius:2px;
  background:#7d1f16;color:#ffd9c8;font-size:9.5px;letter-spacing:.08em}
.tag.mouth .tag-clock{font-size:9.5px;color:#9a917f;letter-spacing:.04em}

/* ---------------------------------------------------------------- npc barks */
/* What a townsperson mutters to nobody, above their own nameplate — drawn as a
   COMIC SPEECH BALLOON, which is the same material every line in the
   conversation window is made of (see `.dlgline` below, and --balloon in
   theme.css). Paper fill, ink outline, and a tapered tail hanging off the
   bottom pointing at the head that said it. It never takes the pointer — you
   talk to an NPC by clicking the NPC.

   TWO ELEMENTS. `.bark` is the wrapper ui/labels.ts writes a screen position
   into every frame; `.bark-paper` is the balloon, and the ONLY thing carrying
   the animation. A keyframe on `transform` outranks an inline style, so a pop
   on the wrapper would drop every new bubble at the top-left of the screen for
   the length of the animation — which is exactly what the old `barkIn` did. */
.bark{position:absolute;transform:translate(-50%,-100%);pointer-events:none;
  max-width:230px}
.bark-paper{position:relative;padding:6px 12px 7px;
  background:linear-gradient(180deg,var(--balloon),var(--balloon-lo));
  border:3px solid var(--balloon-ink);
  /* Four corner radii that do not quite agree, which is the cheapest way to
     stop a rounded rectangle from reading as a rounded rectangle. */
  border-radius:22px 25px 22px 25px / 25px 22px 25px 22px;
  color:var(--balloon-text);font-family:var(--comic);font-weight:600;
  font-size:12px;line-height:1.3;text-align:center;
  box-shadow:0 5px 0 -1px #00000055, 0 16px 26px -16px #000,
    inset 0 2px 0 #fffffff0;
  transform-origin:50% 118%;
  animation:balloonPop .24s cubic-bezier(.34,1.5,.5,1) both}
/* The tail, inked. Two triangles: the outer one IS the outline (same colour as
   the border, meeting it seamlessly at the padding edge), the inner one is the
   paper, overlapping 3px up into the balloon so the border is interrupted
   where the tail joins rather than drawn across it. */
.bark-paper::before,.bark-paper::after{content:'';position:absolute;left:50%;
  width:0;height:0;border-style:solid;border-color:transparent}
.bark-paper::before{margin-left:-9px;bottom:-19px;
  border-width:19px 9px 0 9px;border-top-color:var(--balloon-ink)}
.bark-paper::after{margin-left:-6px;bottom:-13px;
  border-width:16px 6px 0 6px;border-top-color:var(--balloon-lo)}
/* A balloon does not fade in, it POPS — overshoot and settle. Scale only:
   opacity alone reads as a HUD element appearing, and the overshoot is the
   whole difference between "a box arrived" and "somebody spoke". */
@keyframes balloonPop{
  0%{opacity:0;transform:scale(.35)}
  55%{opacity:1;transform:scale(1.07)}
  78%{transform:scale(.975)}
  100%{opacity:1;transform:scale(1)}}
/* ---------------------------------------------------------------- alert marks */
/* What a creature makes of you, over its head: a ? while it is working it out,
   a ! the moment it has (ui/labels.ts placeAlert; the state is the server's,
   see server/sim/ai.ts perceive). Same two-element shape as a bark and for the
   same reason — the wrapper takes the projection, the glyph takes the pop —
   and drawn in the balloons' comic hand, so it reads as the same language: a
   thing the creature is "saying". Yellow is a question, red is an answer. */
.alert{position:absolute;transform:translate(-50%,-100%);pointer-events:none}
.alert-mark{font-family:var(--comic);font-weight:900;font-size:30px;line-height:1;
  -webkit-text-stroke:2px #1a1410;paint-order:stroke fill;
  text-shadow:0 3px 0 #00000066,0 0 12px #00000088;
  transform-origin:50% 100%;animation:markPop .28s cubic-bezier(.34,1.5,.5,1) both}
.alert-mark.noticing{color:#ffd23f}
.alert-mark.seen{color:#ff5a4a;font-size:34px}
@keyframes markPop{
  0%{opacity:0;transform:scale(.3) translateY(8px)}
  55%{opacity:1;transform:scale(1.15)}
  78%{transform:scale(.95)}
  100%{opacity:1;transform:scale(1)}}
.floater{position:absolute;left:0;top:0;transform:translate(-50%,-50%);font-size:15px;
  font-weight:700;text-shadow:0 2px 4px #000;will-change:transform,opacity}
.floater.damage{color:#ff9d8f}
.floater.crit{color:#ffd23f;font-size:19px}
.floater.heal{color:#8ef598}
.floater.xp{color:#c7ef7a;font-size:12px;font-weight:600}
/* Coin swept off the floor by walking over it. Written in the same bone-white
   the silver label on the ground uses, so the pile that vanished and the
   number that replaced it are visibly the same thing. */
.floater.silver{color:#d8d2c4;font-size:13px;font-weight:600;letter-spacing:.03em}
/* A blow that arrived at nothing. Deliberately quiet and grey: a miss is
   information, not an event, and it must never shout louder than a hit. */
.floater.miss{color:#b9b2a6;font-size:13px;font-style:italic;letter-spacing:.06em}
/* The two refusals. Steel for a block, so it reads as the shield it came off;
   the dodge borrows the miss's muted italic, because from the attacker's side
   that is exactly what it was. */
.floater.block{color:#ffd9a0;font-size:14px;font-weight:700;letter-spacing:.04em}
.floater.dodge{color:#a8c8b8;font-size:13px;font-style:italic;letter-spacing:.06em}
/* A control effect that rolled and did not take. Cool and steady against the
   hot damage numbers, because it is the TARGET's refusal rather than the
   caster's failure -- and readable at a glance, since a player who cannot see
   this cannot tell a resistance from a bug. */
.floater.resist{color:#9fd0e8;font-size:14px;font-weight:600;letter-spacing:.05em}

/* ---------------------------------------------------------------- fps / net chip */
/* hud.css positions #fpsChip; ours carries a whole debug line, so let it grow. */
#fpsChip{max-width:none;white-space:nowrap}

/* ---------------------------------------------------------------- select-screen hero */
/* The character-select stage shows a live 3D turntable instead of a sprite. */
.cs-hero3d{width:340px;height:430px;display:block;
  filter:drop-shadow(0 14px 12px #000a) drop-shadow(0 0 18px #b98cff44)}
/* our stage anchors the preview by its bottom edge, like the sprite it replaces */
#csHero.hero3d{margin-left:-170px;bottom:calc(24% - 26px)}

/* character-select fallback (assets still loading): a big rune on the platform */
.cs-sprite.fallback{font-size:64px;color:var(--acc);
  filter:drop-shadow(0 0 22px var(--acc)) drop-shadow(0 8px 8px #000a)}

/* ---------------------------------------------------------------- class portraits */
/* The three roots as a portrait row along the bottom of the create modal, the way the
 * classic creation sheets line their races up. --cc is the class colour; the chosen one
 * wears the gold frame, the others their own accent on hover. */
.cc-rootlab{margin-top:10px}
.ccls-row{display:flex;gap:10px;margin:6px 0 10px}
.ccls{flex:1;display:flex;align-items:center;gap:12px;text-align:left;
  padding:8px 12px 8px 8px;border:1px solid var(--line2);border-radius:12px;
  background:#0f0b1e;cursor:pointer;transition:.15s;color:var(--ink)}
.ccls:hover{border-color:var(--cc,#b98cff);background:#16102b}
.ccls.sel{border-color:var(--gold);background:linear-gradient(180deg,#1c1c44,#141535);
  box-shadow:0 0 0 1px var(--gold) inset,0 0 18px -6px var(--gold)}
.cclsfr{width:56px;height:56px;flex:none;display:grid;place-items:center;border-radius:10px;
  border:1px solid var(--line);background:radial-gradient(70% 70% at 50% 40%,#2a1c44,#0e0713);
  transition:.15s}
.ccls:hover .cclsfr{border-color:var(--cc,#b98cff)}
.ccls.sel .cclsfr{border-color:var(--gold);box-shadow:0 0 12px -4px var(--gold)}
.ccls img.cclsic{width:44px;height:44px;image-rendering:auto;
  filter:drop-shadow(0 2px 4px #000)}
.ccls b{display:block;font-size:13px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--cc,#b98cff)}
.ccls.sel b{color:var(--gold2)}
.ccls span{display:block;font-size:10.5px;color:var(--dim);line-height:1.35;margin-top:2px}
/* Three columns now — sheet, hero, look — so the box is wide rather than tall.
 * box-sizing so the 24px frame border counts toward the calc(var(--vh) * 92) ceiling; the box
 * itself scrolls only as a last resort (the options column scrolls first). */
#csCreate .box{width:980px;max-width:calc(var(--vw) * 94);box-sizing:border-box;padding:20px 22px;
  max-height:min(calc(var(--vh) * 92),900px);overflow:auto}
#csCreate .sub{margin-bottom:12px}
.cc-main{display:flex;gap:14px;min-height:0}
/* under a narrow virtual viewport the sheet takes the full width and the hero drops below */
:root[data-vp~="w1240"] .cc-main{flex-wrap:wrap}
:root[data-vp~="w1240"] .cc-sheet{width:100%}

/* ------------------------------------------------- create modal: the class sheet */
/* The passport on the left: who this root is (the 2D game's attribute vector), what a
 * level-1 character of it opens with (real sums over the kit — shared classOpeningStats),
 * and nine arts it grows into as a 3x3 of the tree's own icons, with the one under the
 * pointer described beneath. --cc is the class colour, --el each art's element. */
.cc-sheet{flex:none;width:292px;display:flex;flex-direction:column;gap:6px;padding:10px 12px;
  border:1px solid var(--line);border-radius:10px;background:#0e0a1cbb;min-height:0}
.cc-shead{display:flex;align-items:center;gap:10px}
.cc-shead img.cc-shic{width:36px;height:36px;image-rendering:auto;flex:none;
  filter:drop-shadow(0 2px 4px #000)}
.cc-shead b{display:block;font-size:17px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--cc,var(--acc));line-height:1.1}
.cc-shead span{display:block;font-size:10px;letter-spacing:.12em;text-transform:uppercase;
  color:#8a7a63;margin-top:2px}
.cc-sblurb{font-size:11px;color:var(--dim);line-height:1.45}
/* section rules — the gold caps line the sheet is divided by */
.cc-sh{display:flex;justify-content:space-between;align-items:baseline;margin-top:4px;
  padding-bottom:3px;border-bottom:1px solid var(--edge);
  font-size:10px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;color:var(--gold)}
.cc-sh small{font-size:9.5px;font-weight:600;letter-spacing:.04em;text-transform:none;
  color:var(--faint)}
/* attribute rows: STR  Strength  [bar]  5 — the class's strongest share fills gold */
.cc-attrs{display:flex;flex-direction:column;gap:3px}
.cc-attr,.cc-vital{display:flex;align-items:center;gap:7px;font-size:11px;line-height:18px}
.cc-attr .k{width:26px;flex:none;font-size:9.5px;font-weight:800;letter-spacing:.06em;
  color:var(--cc,var(--acc))}
.cc-attr .n,.cc-vital .n{width:62px;flex:none;color:var(--dim)}
.cc-attr .bar,.cc-vital .bar{flex:1;height:8px}
.cc-attr .bar>i{background:linear-gradient(180deg,#d9d2c4,#8a7a63)}
.cc-attr.top .bar>i{background:linear-gradient(180deg,var(--gold2),var(--gold))}
.cc-attr b,.cc-vital b{width:34px;flex:none;text-align:right;color:var(--ink);
  font-variant-numeric:tabular-nums}
/* the four plain numbers, two by two */
.cc-stats{display:grid;grid-template-columns:1fr 1fr;gap:4px 8px}
.cc-stat{display:flex;justify-content:space-between;align-items:baseline;padding:2px 8px;
  border:1px solid var(--line);border-radius:6px;background:#0e0a1c;font-size:10.5px;color:var(--dim)}
.cc-stat b{color:var(--gold2);font-size:12px;font-variant-numeric:tabular-nums}
/* the opening-weapon stepper — ◀ name ▶, what is in each hand, and what it is for.
 * Reuses the look customizer's stepper chrome (.cc-step/.cc-sarr/.cc-smid) so the two
 * choices on this screen are visibly the same kind of control. */
.cc-weapon{display:flex;flex-direction:column;gap:5px}
.cc-weapon .cc-sval{font-size:12.5px;font-weight:700;color:var(--gold2)}
/* a class with one loadout keeps its arrows, drawn dead: a control that vanishes
 * reads as a bug, one that is plainly disabled reads as the answer */
.cc-sarr.off{opacity:.28;cursor:default}
.cc-sarr.off:hover{border-color:var(--line2);color:var(--dim);background:#0f0b1e}
.cc-sarr.off:active{transform:none}
.cc-whands{display:flex;flex-wrap:wrap;gap:4px 6px}
.cc-wit{display:flex;align-items:center;gap:4px;padding:2px 7px 2px 3px;
  border:1px solid var(--line);border-radius:999px;background:#0e0a1c;
  font-size:10px;color:var(--dim);white-space:nowrap}
.cc-wit img.cc-witic{width:16px;height:16px;object-fit:contain;image-rendering:auto}
.cc-wblurb{font-size:10.5px;line-height:1.4;color:var(--faint)}

/* the nine arts: a 3x3 of the tree's own icons under the hero, the one under the
 * pointer described in the box beside it, which is as tall as the grid and scrolls */
.cc-right{flex:1;min-width:0;display:flex;flex-direction:column;gap:12px}
.cc-artsblock{display:flex;flex-direction:column;gap:6px}
.cc-artsrow{display:flex;gap:10px;align-items:stretch}
.cc-arts{flex:none;width:300px;display:grid;grid-template-columns:repeat(3,1fr);gap:5px}
.cc-art{position:relative;display:flex;flex-direction:column;align-items:center;gap:2px;
  min-width:0;padding:5px 3px 4px;border:1px solid var(--line);border-radius:8px;
  background:#0f0b1c;cursor:pointer;color:var(--ink);transition:.15s}
.cc-art:hover{border-color:var(--el,var(--gold));background:#16102b}
.cc-art.sel{border-color:var(--el,var(--gold));background:#16102b;
  box-shadow:0 0 0 1px var(--el,var(--gold)) inset,0 0 12px -5px var(--el,var(--gold))}
.cc-art .ic{position:relative;width:36px;height:36px;display:grid;place-items:center;
  border-radius:7px;border:1px solid var(--el,var(--line2));background:#0a0714;
  box-shadow:0 0 10px -4px var(--el,transparent)}
/* the tree glyph beneath the PNG: what shows when the PNG does not arrive */
.cc-art .ic .gl{position:absolute;inset:0;display:grid;place-items:center;
  font-style:normal;font-size:18px;line-height:1;color:var(--el,var(--dim));opacity:.85}
.cc-art .ic img.abico{position:relative;width:32px;height:32px;display:block;
  object-fit:contain;image-rendering:auto;background:#0a0714;border-radius:5px}
.cc-art .nm{max-width:100%;font-size:9.5px;line-height:1.15;text-align:center;color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cc-art .tg{font-size:8px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}
.cc-art.known .tg{color:var(--good)}
/* the description box beside the grid — hover borrows it, a click keeps it */
.cc-artdesc{flex:1;min-width:0;max-height:220px;overflow-y:auto;padding:7px 9px;
  border:1px solid var(--line);border-radius:8px;background:#0e0a1c;
  font-size:10.5px;line-height:1.45;color:var(--dim);
  scrollbar-width:thin;scrollbar-color:#4a3b28 transparent}
.cc-artdesc b{font-size:12px;margin-right:6px}
.cc-artdesc em{font-style:normal;font-size:9.5px;color:var(--faint)}
.cc-artdesc .meta{display:block;margin:1px 0 2px;font-size:9.5px;letter-spacing:.04em;
  color:var(--gold)}
.cc-artdesc .d{display:block;color:var(--dim)}

/* ------------------------------------------------- create modal: the look */
/* Arrow-stepper layout: body toggles top-left over the framed preview (turn
 * arrows flanking it), ◀ label/value ▶ stepper rows + round color dots on the
 * right, Reset / Randomise under the rows. Emberbound's own chrome throughout.
 * The preview cell is 204x258 — exactly 3/5 of the hero canvas's 340x430
 * render, so both axes land on whole pixels. */
.cc-custom{display:flex;gap:14px;min-height:0}
/* 284 = 34px arrow + 6px gap + 204px frame + 6px + 34px arrow */
.cc-preview{flex:none;width:284px;display:flex;flex-direction:column;gap:6px}
/* the look column: the steppers, then Reset / Randomise right under them */
.cc-look{flex:1;min-width:0;display:flex;flex-direction:column;gap:10px}
.cc-look .cc-opts{flex:none}

/* body build toggles — two squares, aligned with the frame's left edge */
.cc-bodyrow{display:flex;gap:6px;margin-left:40px}
.cc-body{width:40px;height:40px;flex:none;display:grid;place-items:center;padding:0;
  border:1px solid var(--line2);border-radius:8px;background:#0f0b1e;cursor:pointer;
  transition:.15s}
.cc-body:hover{border-color:var(--gold);background:#16102b}
.cc-body.sel{border-color:var(--gold);background:linear-gradient(180deg,#241d10,#1a1408);
  box-shadow:0 0 0 1px var(--gold) inset}
/* gender symbols: cool blue Mars, warm rose Venus; gold when chosen */
.cc-gender{font-size:22px;line-height:1;font-weight:700;
  text-shadow:0 1px 2px #00000080}
.cc-gender.broad{color:#7fb1e0}
.cc-gender.slim{color:#e08cb4}
.cc-body.sel .cc-gender{color:var(--gold2)}

/* the frame, flanked by the turntable arrows */
.cc-prevrow{display:flex;align-items:center;gap:6px}
.cc-turn{width:34px;height:34px;flex:none;display:grid;place-items:center;padding:0;
  border:1px solid var(--line2);border-radius:8px;background:#0f0b1e;color:var(--dim);
  font-size:12px;line-height:34px;cursor:pointer;transition:.15s}
.cc-turn:hover{border-color:var(--gold);color:var(--gold2);background:#16102b}
.cc-turn:active{transform:translateY(1px)}
.cc-prevcell{position:relative;width:204px;height:258px;display:grid;place-items:center;
  overflow:hidden;border:1px solid #4a3b28;border-radius:8px;
  background:radial-gradient(90% 70% at 50% 62%,#2a1c33,#0e0713)}
/* the renderer stamps its 340x430 as an inline style; !important scales it */
#ccPrev .cs-hero3d{width:204px!important;height:258px!important;
  filter:drop-shadow(0 8px 8px #000a) drop-shadow(0 0 12px #b98cff33)}
/* the waiting rune is composed for the big stage; stand it upright in the cell */
#ccPrev .cs-sprite.fallback{transform:none;font-size:44px}
.cc-prevsub{font-size:10px;letter-spacing:1.2px;text-transform:uppercase;
  text-align:center;color:#8a7a63;line-height:15px}

/* the options column scrolls; the modal stays inside the viewport */
.cc-opts{flex:1;min-width:0;max-height:min(392px,calc(var(--vh) * 46));overflow-y:auto;padding-right:6px;
  scrollbar-width:thin;scrollbar-color:#4a3b28 transparent}
.cc-opts .au-fld.cc-sec{margin-bottom:12px}

/* stepper rows — ◀ LABEL value ▶ on a sunken plate */
.cc-step{display:flex;align-items:center;gap:6px}
.cc-sarr{width:28px;height:28px;flex:none;display:grid;place-items:center;padding:0;
  border:1px solid var(--line2);border-radius:7px;background:#0f0b1e;color:var(--dim);
  font-size:11px;line-height:28px;cursor:pointer;transition:.15s}
.cc-sarr:hover{border-color:var(--gold);color:var(--gold2);background:#16102b}
.cc-sarr:active{transform:translateY(1px)}
.cc-smid{flex:1;min-width:0;height:28px;display:flex;align-items:center;
  justify-content:center;gap:8px;padding:0 8px;border:1px solid var(--line);
  border-radius:7px;background:#0e0a1c}
.cc-slab{font-size:10px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;
  color:#8a7a63;line-height:16px;white-space:nowrap}
.cc-sval{font-size:12px;font-weight:600;color:var(--ink);line-height:16px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* round color dots — the palette rgb is painted inline; .sel wears the ring */
.cc-dotrow{display:flex;flex-wrap:wrap;gap:5px}
.cc-step+.cc-dotrow{margin-top:7px}
.cc-swb{width:18px;height:18px;flex:none;padding:0;border-radius:50%;cursor:pointer;
  border:1px solid #000c;box-shadow:inset 0 1px 0 #ffffff2e,0 1px 2px #0008;
  transition:box-shadow .12s}
.cc-swb:hover{box-shadow:0 0 0 1px var(--gold),inset 0 1px 0 #ffffff2e}
.cc-swb.sel{box-shadow:0 0 0 2px var(--gold),inset 0 1px 0 #ffffff2e}

/* reset / randomise, under the steppers; Create stays in the footer */
.cc-toolrow{display:flex;gap:8px}

/* the footer: the name grows, the actions keep the right; an error takes a row of its own */
.cc-foot{display:flex;flex-wrap:wrap;align-items:center;gap:8px 12px;margin-top:4px}
.cc-foot .cc-namefld{flex:1 1 260px;margin:0}
.cc-foot .au-err{flex:1 1 100%;order:3;margin:0}
#csCreate .cc-foot .acts{margin-top:0}

/* ---------------------------------------------------------------- item cells */
/* Tier chip in the corner of an item cell (our tiers, not the 2D game's). */
.slotcell .tq{position:absolute;left:3px;top:2px;font-size:9px;font-weight:700;
  letter-spacing:.02em;color:var(--dim);text-shadow:0 1px 2px #000}
.slotcell img.itic{image-rendering:auto;width:72%;height:72%;object-fit:contain}

/* ---------------------------------------------------------------- abilities tab */
/* The arts compendium rows in the Character window. --el is the art's accent. */
.abrow{display:flex;align-items:center;gap:10px;padding:7px 9px;margin:3px 8px;
  border:1px solid var(--line);border-radius:9px;background:#16102acc;cursor:pointer;
  transition:border-color .15s}
.abrow:hover{border-color:var(--el,var(--gold))}
.abrow.locked{opacity:.5;cursor:default;border-style:dashed}
.abrow .abic{width:34px;height:34px;flex:0 0 auto;display:grid;place-items:center;
  background:#0f0b1c;border:1px solid var(--el,var(--line2));border-radius:8px;
  box-shadow:0 0 10px -4px var(--el,transparent)}
.abrow .abic img{width:80%;height:80%;object-fit:contain;image-rendering:auto}
.abrow .abmeta{flex:1;min-width:0}
.abrow .abmeta b{font-size:12.5px;color:var(--el,var(--ink))}
.abrow .abmeta small{color:var(--faint);font-size:10px;margin-left:8px}
.abrow .abmeta span{display:block;color:var(--dim);font-size:11px;line-height:1.35}
.abrow em{flex:0 0 auto;font-style:normal;font-size:9.5px;color:var(--gold);
  letter-spacing:.06em;text-transform:uppercase;text-align:right;max-width:110px}
.abrow.locked em{color:var(--faint)}
#itemsPaneAbil{overflow-y:auto}

/* ---------------------------------------------------------------- ability tree
   A node is indented by its depth, so a branch reads as a branch without any
   layout work; the rail on the left is what makes the descent legible. Learned
   nodes take the gold border, available ones stay clickable, locked ones say
   why on the right. */
/* The Abilities pane is the ported my-mmorpg board (css/abilities.css) —
   a flex column of the points bar over the pannable canvas. */
#itemsPaneAbil{display:flex;flex-direction:column;overflow:hidden;padding:0}
#itemsPaneSkills{padding:10px 14px;overflow-y:auto}

/* ---------------------------------------------------------------- hotbar icons */
#actionbar .slot .ic img.abico{width:100%;height:100%;object-fit:contain;
  image-rendering:auto;filter:drop-shadow(0 1px 2px #000)}


/* ---------------------------------------------------------------- cast bar
   Sits just above the action bar, where the eye already is during a fight, and
   only exists while a wind-up is running. The fill is a CSS transition rather
   than a per-frame update: the server told us exactly how long it will take. */
/* 14px above whatever stands at the bottom centre — the hotbar, or the orbs
   and the triad bars that ride on it — which ui/hud.ts measures and publishes
   as --bottomclear on every layout. At a fixed bottom:104px the bar ran 13px
   into the orbs; at a dock-relative guess it landed on them again the moment
   the hotbar grew a second row and carried the orbs up with it. The action
   prompt stands 30px higher still (hud.css), so the two never meet. */
/* THE LOOK: a brass tube filling with molten gold, capped by an arrowhead at
   each end. The old bar was a violet-to-gold gradient in a lilac rounded rect,
   which read as a progress widget from some other program — nothing else in
   the HUD is that colour, and a wind-up is the one moment the eye is furthest
   from the bottom of the screen and needs the shape, not the hue, to carry it.
   The palette is the kit's own (--gold/--gold2/--gold3), so the bar belongs to
   the same metal as the frames and the buttons around it.

   Everything here is drawn in CSS rather than cut from castbar-frame.png. That
   sheet is a 378x94 ornament with spear finials, built for a boss-cast banner
   across the top of the screen; stretched into a 300x22 strip above the hotbar
   its runes turn to mud and its border-image slices eat 55px of each end. The
   gradients below cost nothing, scale with --uiscale exactly, and recolour on
   one line if a cast ever needs to say something other than "gold". */
.castbar{position:fixed;left:50%;transform:translateX(-50%);
  bottom:calc(var(--bottomclear,214px) + 14px);
  width:min(300px,calc(var(--vw) * 52));height:22px;z-index:60;
  filter:drop-shadow(0 2px 10px #000c)}
.castbar.hidden{display:none}
/* The arrowheads. They sit OUTSIDE the track, which is why the track carries
   the overflow clip and this element does not: an ::after pinned to a
   `overflow:hidden` box is clipped away with the fill. clip-path rather than
   the border triangle trick so the shape keeps its gradient and its width is
   one number to change. */
.castbar::before,.castbar::after{content:'';position:absolute;top:50%;
  width:11px;height:16px;margin-top:-8px;
  background:linear-gradient(180deg,var(--gold2) 0%,var(--gold) 46%,#8f5a15 100%)}
.castbar::before{right:100%;margin-right:2px;
  clip-path:polygon(100% 0,100% 100%,0 50%)}
.castbar::after{left:100%;margin-left:2px;
  clip-path:polygon(0 0,0 100%,100% 50%)}
/* The tube. A hairline of dark brass outside, the oxblood channel inside, and
   an inset shadow along the top so the empty stretch reads as a hollow rather
   than as a painted stripe. */
.castbar .cbtrack{position:absolute;inset:0;overflow:hidden;
  border:1px solid var(--gold3);border-radius:3px;
  background:linear-gradient(180deg,#5a2410 0%,#3b1409 55%,#48190c 100%);
  box-shadow:inset 0 2px 4px -1px #000a,inset 0 0 0 1px #0007}
/* The molten stretch: a glossy tube, not a flat block. The bright band sits
   ABOVE centre because that is where a cylinder catches the light, and the
   thin lip at the leading edge is what makes the fill look like it is being
   poured rather than merely revealed. */
.castbar .cbfill{height:100%;width:0;
  background:linear-gradient(180deg,
    #b8781f 0%,var(--gold2) 30%,#ffe9a8 44%,
    var(--gold) 62%,#c07f1c 88%,#8f5a15 100%);
  /* The head of the pour: a hot lip, then a short bloom of light thrown onto
     the empty channel ahead of it. Without these the fill is a rectangle that
     changes width, and the eye reads the EDGE of a cast bar — it is the only
     part that moves. */
  box-shadow:inset -2px 0 0 #fff6d8, 2px 0 7px -1px #ffbe4dcc}
.castbar .cbname{position:absolute;inset:0;display:grid;place-items:center;
  font-size:11px;letter-spacing:.09em;text-transform:uppercase;font-weight:700;
  color:#fff6e2;
  /* A true four-way BLACK outline, not a drop shadow and not a coloured one.
     The name crosses both materials as the fill passes under it, and the first
     attempt outlined it in the track's own oxblood: over the dark stretch that
     is invisible and fine, over the bright band it turned every letter into a
     smear. Black holds on gold, which is the whole reason gold UI text is
     outlined in black everywhere else in this kit. */
  text-shadow:1px 0 0 #000,-1px 0 0 #000,0 1px 0 #000,0 -1px 0 #000,
    0 0 4px #000c}


/* ---------------------------------------------------------------- npc dialogue
   Class names carried over from the 2D game (.dlgline.me / .npc, .kw, .topic)
   so its GUI kit styling applies rather than being reinvented. The window sits
   low and left of centre, clear of the action bar and the chat console. */
/* The wrapper is a plain full-screen .overlay now — fixed, inset 0, click
   through — and the BOX carries the position, which is what every other window
   in the game does and what the drag helper expects to write into. It used to
   be the wrapper that was positioned, with the box absolute inside it at no
   offset, so there was nothing for a drag to move. */
#npcDlgWin{z-index:70}
#npcDlgWin.hidden{display:none}
/* A FIXED HEIGHT, not a maximum.
 *
 * The log was capped rather than sized, so the window was as tall as the
 * conversation: it opened at one line, grew on every reply, and jumped again
 * the first time a topic chip appeared. That is bad enough anchored to the
 * bottom of the screen and worse once the window has been dragged somewhere,
 * because the thing you placed is not the thing you get back. 430px is what
 * the old maximum came to with the bar, a row of chips and the say line, so
 * the window is now always the size it used to reach at its largest. */
#npcDlgWin .dlgbox{left:50%;bottom:150px;transform:translateX(-50%);
  width:min(560px,calc(var(--vw) * 92));height:min(430px,calc(var(--vh) * 60));display:flex;flex-direction:column}
/* flex:1 to absorb whatever the chip row is not using; min-height:0 because a
   flex item will not scroll below its content without it. */
/* The transcript is a COMIC STRIP: every line is a speech balloon of the same
   paper the bark bubble over the NPC's head is made of, the NPC's on the left
   with its tail down-left, yours on the right with its tail down-right. That
   is the reading order a comic panel has trained everyone in, and it does the
   job a wall of same-coloured transcript cannot — you can tell at a glance who
   said what without reading a word of it.

   The halftone under it is one radial-gradient dot per 6px, at a hair over 5%
   white. Barely visible on purpose: it should read as newsprint behind the
   balloons, not as a pattern anybody notices. */
#npcDlgWin .dlglog{flex:1;min-height:0;overflow-y:auto;padding:14px 16px 4px;
  background-image:radial-gradient(#ffffff0f 1px,transparent 1.2px);
  background-size:6px 6px}
.dlgline{position:relative;width:fit-content;max-width:78%;
  margin:0 0 26px;padding:9px 15px 10px;
  background:linear-gradient(180deg,var(--balloon),var(--balloon-lo));
  border:3px solid var(--balloon-ink);
  border-radius:20px 23px 20px 23px / 23px 20px 23px 20px;
  color:var(--balloon-text);font-family:var(--comic);font-size:13px;
  line-height:1.35;
  box-shadow:0 5px 0 -1px #00000055, 0 18px 30px -18px #000,
    inset 0 2px 0 #fffffff0;
  transform-origin:0% 100%;
  animation:balloonPop .26s cubic-bezier(.34,1.5,.5,1) both}
.dlgline.me{margin-left:auto;transform-origin:100% 100%}
/* Who is speaking, as a caption inside the balloon rather than a colour on the
   text. Comics do this with the tail alone; a transcript that is read back
   through wants the name, so it is here and quiet — the UI face, not the comic
   one, so it never competes with the line it labels. */
.dlgline b{display:block;margin:0 0 3px;font-family:var(--ui);font-size:9.5px;
  font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--balloon-dim)}
.dlgline.npc b{color:var(--gold3)}
.dlgline .kw{color:#7a4a12;font-weight:700;
  text-decoration:underline dotted;cursor:pointer}
/* The tails. Ink triangle first (it IS the outline, and starts at the padding
   box so it meets the 3px border seamlessly), paper triangle over it, inset
   3px and overlapping up into the balloon so the border opens into the tail
   instead of being drawn across its mouth. */
.dlgline::before,.dlgline::after{content:'';position:absolute;
  width:0;height:0;border-style:solid;border-color:transparent}
.dlgline.npc::before{left:24px;bottom:-19px;
  border-width:19px 16px 0 0;border-top-color:var(--balloon-ink)}
.dlgline.npc::after{left:27px;bottom:-13px;
  border-width:16px 12px 0 0;border-top-color:var(--balloon-lo)}
.dlgline.me::before{right:24px;bottom:-19px;
  border-width:19px 0 0 16px;border-top-color:var(--balloon-ink)}
.dlgline.me::after{right:27px;bottom:-13px;
  border-width:16px 0 0 12px;border-top-color:var(--balloon-lo)}

/* ---- the topic chips
   WHAT A COLOUR MEANS HERE. Gold is work being offered, blue is handing work
   in or being told a rule you can act on, and the coin opens a shop. The kind
   is decided by the SERVER and travels with the chip (DialogueChip in shared),
   because honour has already chosen which tier of a topic this player is
   being offered and a colour derived client-side could contradict the line
   above it. Everything else — the lore, which is most of it — stays plain, and
   that restraint is what makes the two colours mean anything.

   Every lit chip also carries a glyph, because a colour on its own fails for
   the colour-blind, fails at a glance on a busy screen, and fails for anybody
   who has not learned the palette yet. */
#npcDlgWin .dlgtopics{display:flex;flex-wrap:wrap;gap:6px;padding:8px 14px 9px;
  border-top:1px solid var(--line)}
#npcDlgWin .dlgtopics:empty{display:none}
.topic{display:inline-flex;align-items:center;gap:5px;
  font:inherit;font-size:11px;letter-spacing:.04em;text-transform:lowercase;
  color:var(--ink);background:#1b1430;border:1px solid var(--line2);border-radius:999px;
  padding:3px 11px;cursor:pointer;transition:border-color .15s,background .15s,box-shadow .15s}
.topic:hover{border-color:var(--gold);background:#241a3c}
.topic .tmark{font-style:normal;font-weight:800;font-size:11px;line-height:1;
  display:grid;place-items:center;width:14px;height:14px;border-radius:50%;
  background:#00000059}
/* gold — there is work here */
.topic.quest{color:#ffe0ac;border-color:var(--gold);background:#2b2010;
  box-shadow:0 0 12px -5px var(--gold), inset 0 0 0 1px #ffffff10}
.topic.quest:hover{background:#3a2b12;box-shadow:0 0 14px -3px var(--gold)}
.topic.quest .tmark{background:var(--gold);color:#20180a}
/* blue — hand it in, or be taught something you can use */
.topic.turnin,.topic.learn{color:#cfe6ff;border-color:#4f86c6;background:#132133;
  box-shadow:0 0 12px -5px #4f86c6, inset 0 0 0 1px #ffffff10}
.topic.turnin:hover,.topic.learn:hover{background:#1a2e46;box-shadow:0 0 14px -3px #6aa6e8}
.topic.turnin .tmark,.topic.learn .tmark{background:#5b95d6;color:#0c1622}
/* the same blue family, because a shop is a service and not a story — but the
   coin says which service without anybody having to hover it */
.topic.trade{color:#d7e9ff;border-color:#5f93c9;background:#152437}
.topic.trade:hover{background:#1d3350}
.topic.trade .tmark{background:linear-gradient(180deg,var(--gold2),var(--gold));
  color:#2a1e08;font-size:12px}
.topic.locked{opacity:.5;cursor:default}
#npcDlgWin .dlgsay{padding:0 14px 12px}
#npcDlgWin .dlgsay input{width:100%;font-family:var(--comic);font-size:12.5px;
  color:var(--balloon-text);
  background:linear-gradient(180deg,var(--balloon),var(--balloon-lo));
  border:2px solid var(--balloon-ink);border-radius:14px;padding:6px 12px;
  box-shadow:inset 0 2px 0 #fffffff0}
#npcDlgWin .dlgsay input::placeholder{color:#a1957e}
#npcDlgWin .dlgsay input:focus{outline:none;border-color:var(--gold3);
  box-shadow:inset 0 2px 0 #fffffff0, 0 0 0 2px #e0a44c4d}

/* ----------------------------------------------------------------- party frame */
/* Who you are travelling with, down the left side under the portrait.
 *
 * The one thing this frame shows that no other frame has to: WHERE somebody
 * is. Ashvault is phased, so a party can be split across two versions of the
 * same city — and a member standing in the other one must read as absent
 * rather than as a live health bar that never moves. That is `.away`. */
/* Steps down while the dungeon bar (top:184, 31 tall) is up: both live in
   the strip under the portrait, and a party IS the case a dungeon bar is up. */
body:has(#dungeonbar) #partyframe{top:232px}
#partyframe{left:12px;top:210px;width:212px;padding:5px 7px 7px;
  display:flex;flex-direction:column;gap:4px}
#partyframe .pmhead{display:flex;align-items:center;gap:6px;padding:0 2px 3px;
  font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim);
  border-bottom:1px solid #ffffff12}
#partyframe .pmhead b{color:#c9c2b4;font-weight:600}
#partyframe .pmmore{margin-left:auto;width:18px;height:16px;line-height:14px;padding:0;
  border:1px solid #ffffff1c;border-radius:4px;background:#0006;color:var(--dim);
  font-size:11px;cursor:pointer}
#partyframe .pmmore:hover{color:#e8e4d8;border-color:#ffffff40}
/* One member: class swatch, then the name and two bars. The row IS a target. */
#partyframe .pm{display:flex;align-items:stretch;gap:6px;padding:3px 3px 3px 0;
  border-radius:5px;cursor:pointer;border-left:3px solid var(--cc,#9aa0ac)}
#partyframe .pm:hover,#partyframe .pm:focus-visible{background:#ffffff0d;outline:none}
#partyframe .pmcls{flex:none;width:22px;display:flex;align-items:center;justify-content:center}
#partyframe .pmic{width:18px;height:18px;filter:drop-shadow(0 1px 1px #000c)}
#partyframe .pmbody{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
#partyframe .pmtop{display:flex;align-items:baseline;justify-content:space-between;gap:6px}
#partyframe .pmname{font-size:11.5px;font-weight:600;color:#e8e4d8;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#partyframe .pmcrown{font-style:normal;color:var(--gold2);margin-right:3px;font-size:10px}
#partyframe .pm.you .pmname{color:var(--gold2)}
#partyframe .pmlvl{font-size:9.5px;color:var(--dim);flex:none}
#partyframe .pmbar{position:relative;border-radius:3px;background:#1a1220;
  box-shadow:inset 0 0 0 1px #0008;overflow:hidden}
#partyframe .pmbar.hp{height:10px}
#partyframe .pmbar.en{height:4px}
#partyframe .pmbar i{display:block;height:100%;transition:width 180ms linear}
#partyframe .pmbar.hp i{background:linear-gradient(180deg,#7de08a,#3f8f52)}
#partyframe .pmbar.en i{background:linear-gradient(180deg,#7fb4ff,#3a63b8)}
#partyframe .pmbar.hp span{position:absolute;inset:0;font-size:8px;line-height:10px;
  text-align:center;color:#fff;text-shadow:0 1px 1px #000c;letter-spacing:.04em}
#partyframe .pm.low .pmbar.hp i{background:linear-gradient(180deg,#ff8d7a,#b0392a);
  animation:pmlow 900ms ease-in-out infinite alternate}
@keyframes pmlow{from{filter:brightness(1)}to{filter:brightness(1.45)}}
#partyframe .pmwhere{font-size:8.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--dim)}
#partyframe .pm.away{opacity:.55}
#partyframe .pm.away .pmbar i{background:#4a4452}
#partyframe .pm.dead .pmbar i{background:#5a2a2a;width:100%!important}
#partyframe .pm.dead .pmname{color:#c98a80}
/* The tooltip's stat line, under the coloured chips. */
.tip .tipline{margin-top:5px;color:var(--dim);font-size:11px}

/* The invitation. A prompt, because it needs an answer. */
#partyinvite{position:fixed;left:50%;top:22%;transform:translateX(-50%);z-index:60;
  background:var(--panel);border:1px solid var(--gold3);border-radius:8px;
  padding:12px 16px 11px;box-shadow:0 10px 30px #000a;text-align:center}
#partyinvite p{margin:0 0 9px;font-size:12.5px;color:#e8e4d8}
#partyinvite b{color:var(--gold2)}
#partyinvite .pirow{display:flex;gap:8px;justify-content:center}.skrow.sub{padding-left:14px;opacity:.75;font-size:.92em}


/* ------------------------------------------------- target frame: worn effects
   What is running on the body you have locked. It answers the one question a
   fight in this game turns on -- did my stun land, and is theirs still on me --
   and until the targetEffects message existed no client could see it at all.
   Glyphs rather than words: the row has to be read in the half second between
   deciding to press something and pressing it. */
#targetframe .tf-fx{display:flex;gap:3px;margin-top:4px;flex-wrap:wrap}
#targetframe .tf-fx .fx{display:inline-flex;align-items:center;justify-content:center;
  width:16px;height:16px;border-radius:3px;font-size:11px;line-height:1;
  background:#0006;border:1px solid #ffffff1f;color:#dfe6f2}
/* A debuff is the half you are looking for, so it is the half that is loud. */
#targetframe .tf-fx .fx.bad{color:#ffb3a6;border-color:#ff6a4d55;background:#3a0f0a66}

/* --------------------------------------------------------------- kill feed
   Every frame of the Lineage 2 reference fights carries one of these across
   the top. It is not decoration: an open world where kills happen in silence
   has no politics in it, because nobody can tell who is doing what to whom.

   Under the top bar (which owns y 6..73) and clear of the target frame at 96
   by sitting to the RIGHT of centre rather than on it -- the middle of the
   screen belongs to the fight. */
#killfeed{position:absolute;top:78px;right:18px;z-index:60;pointer-events:none;
  display:flex;flex-direction:column;align-items:flex-end;gap:3px;max-width:38%}
#killfeed .kf{font-size:12px;letter-spacing:.02em;color:#cfc8b8;
  text-shadow:0 1px 3px #000,0 0 8px #000;opacity:0;animation:kfIn 8s ease-out forwards;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
#killfeed .kf b{font-weight:700;color:#e6dfd0}
/* A fight and a murder must not read alike -- that difference IS the honour
   system, said in the one place everybody looks. */
#killfeed .kf.pvp b.k{color:#c58cff}
#killfeed .kf.pk b.k{color:#ff3b30}
#killfeed .kf.mob b.k{color:#e08a7a}
@keyframes kfIn{0%{opacity:0;transform:translateX(10px)}
  6%{opacity:1;transform:translateX(0)}80%{opacity:1}100%{opacity:0}}
@media (prefers-reduced-motion:reduce){
  #killfeed .kf{animation:kfFade 8s ease-out forwards}
  @keyframes kfFade{0%{opacity:0}6%,80%{opacity:1}100%{opacity:0}}}

/* ------------------------------------------------------------- centre shout
   What just happened TO you, in the one place you cannot miss. Reserved for
   things you must act on THIS second -- the control that landed, the control
   you shrugged off -- and never for anything you could read at leisure, or it
   becomes wallpaper and the one that mattered is missed with the rest.

   Below the zone banner's floor (206px) so a border crossing and a stun do not
   land on each other. */
#shout{position:absolute;top:38%;left:50%;transform:translateX(-50%);z-index:71;
  pointer-events:none;text-align:center;opacity:0}
#shout.on{animation:shoutIn 1.1s ease-out forwards}
#shout b{font-size:26px;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:#ffd9d0;text-shadow:0 2px 6px #000,0 0 20px #ff5a4a88}
#shout.good b{color:#bfe9ff;text-shadow:0 2px 6px #000,0 0 20px #6fd0ff88}
@keyframes shoutIn{0%{opacity:0;transform:translateX(-50%) scale(.9)}
  15%{opacity:1;transform:translateX(-50%) scale(1)}
  65%{opacity:1}100%{opacity:0;transform:translateX(-50%) scale(1.04)}}
@media (prefers-reduced-motion:reduce){
  #shout.on{animation:shoutFade 1.1s ease-out forwards}
  @keyframes shoutFade{0%,100%{opacity:0}15%,65%{opacity:1}}}

/* ------------------------------------------------- the wind and the temper
   Stamina and rage share one element, because they are opposites: you SPEND
   stamina and you EARN rage, so the falling level and the rising one can never
   be confused for each other. Three globes now rather than two -- liquidorb.ts
   arrived from my-mmorpg with a green palette that nothing had ever used. */
.orb.st .lq{filter:drop-shadow(0 0 10px #77dd3a44)}
/* The temper, as a ring around the globe. A conic sweep rather than a second
   liquid: rage is a thing you choose when to spend, not one you watch, and a
   second sloshing surface next to the first would compete with it for the
   glance that matters. */
.orb.st::after{content:'';position:absolute;inset:-3px;border-radius:50%;
  pointer-events:none;
  background:conic-gradient(#ff5a4a calc(var(--rage,0) * 360deg),transparent 0);
  -webkit-mask:radial-gradient(closest-side,transparent calc(100% - 3px),#000 calc(100% - 3px));
  mask:radial-gradient(closest-side,transparent calc(100% - 3px),#000 calc(100% - 3px));
  opacity:.85}
/* In the bar styles the two are drawn as one track: stamina from the left,
   rage from the right, meeting when you are exhausted and furious at once. */
.sbar.st{position:relative;background:#0b0f0acc}
.sbar.st > i{background:linear-gradient(180deg,#a8e05a,#4e8f1e)}
.sbar.st > i.rage{position:absolute;right:0;top:0;height:100%;
  background:linear-gradient(180deg,#ff8a6a,#c0301a);opacity:.9}
.tri.st .sbar.st{height:8px}
