/* Fight and spell feedback over the world: block-letter numbers that pop and arc, words over heads
   in their school's colours, and health bars that drain. Loaded after kit.css, whose older rules for
   svg numbers no longer match (numbers are pictures now) and whose level up banner is kept.
   The effects themselves are pixel sprites and blocks in the scene (client/js/entities.js). */

/* Labels, numbers and words are placed each frame with a transform from the top left corner. */
.label, .splat, .float { left: 0; top: 0; }

/* ---- Numbers: damage pops and arcs aside, a heal rises, a miss shrugs ---- */
.splat { position: absolute; transform: translate(-50%, -50%); width: auto; height: auto; background: none; border: 0; border-radius: 0; line-height: 0; pointer-events: none; }
.splat .blocktext { display: block; margin: 0 auto; height: 22px; animation: fx-pop 0.9s cubic-bezier(0.2, 0.8, 0.3, 1) both; transform-origin: 50% 80%; }
.splat.big .blocktext { height: 30px; animation-duration: 1s; }
.splat.miss .blocktext { height: 18px; animation: fx-shrug 0.8s ease-out both; }
.splat.heal .blocktext { animation: fx-rise 1.15s cubic-bezier(0.2, 0.8, 0.3, 1) both; }
/* Each starts beside the head (--dx is to one side) and moves further out, clear of the name tag. */
@keyframes fx-pop {
  0% { transform: translate(calc(var(--dx, 0px) * 0.45), 10px) scale(0.3); opacity: 0; }
  10% { transform: translate(calc(var(--dx, 0px) * 0.55), -6px) scale(1.5); opacity: 1; }
  20% { transform: translate(calc(var(--dx, 0px) * 0.65), -14px) scale(0.88); }
  32% { transform: translate(calc(var(--dx, 0px) * 0.75), -19px) scale(1.08); }
  48% { transform: translate(calc(var(--dx, 0px) * 0.85), -21px) scale(1); opacity: 1; }
  100% { transform: translate(var(--dx, 0px), -10px) scale(0.9); opacity: 0; }
}
@keyframes fx-rise {
  0% { transform: translate(calc(var(--dx, 0px) * 0.6), 12px) scale(0.4); opacity: 0; }
  14% { transform: translate(calc(var(--dx, 0px) * 0.7), 0) scale(1.25); opacity: 1; }
  28% { transform: translate(calc(var(--dx, 0px) * 0.78), -8px) scale(1); }
  75% { opacity: 1; }
  100% { transform: translate(var(--dx, 0px), -40px) scale(0.95); opacity: 0; }
}
@keyframes fx-shrug {
  0% { transform: translate(calc(var(--dx, 0px) * 0.5), 4px) scale(0.5); opacity: 0; }
  18% { transform: translate(calc(var(--dx, 0px) * 0.6), -8px) scale(1.1) rotate(-8deg); opacity: 1; }
  36% { transform: translate(calc(var(--dx, 0px) * 0.72), -10px) scale(1) rotate(6deg); }
  54% { transform: translate(calc(var(--dx, 0px) * 0.85), -11px) rotate(-3deg); opacity: 1; }
  100% { transform: translate(var(--dx, 0px), -14px) rotate(0deg); opacity: 0; }
}
/* A critical blow: bigger, in honey (red on a player), popping in with a white outline that shrinks away and a
   short shake, then arcing out like any other number. */
.splat.crit { z-index: 2; }
.splat.crit .blocktext { height: 32px; animation: fx-crit 1.15s cubic-bezier(0.2, 0.8, 0.3, 1) both; }
@keyframes fx-crit {
  0% { transform: translate(calc(var(--dx, 0px) * 0.4), 12px) scale(0.3); opacity: 0; filter: drop-shadow(3px 0 0 #fff) drop-shadow(-3px 0 0 #fff) drop-shadow(0 3px 0 #fff) drop-shadow(0 -3px 0 #fff); }
  8% { transform: translate(calc(var(--dx, 0px) * 0.5), -8px) scale(1.6) rotate(-6deg); opacity: 1; filter: drop-shadow(3px 0 0 #fff) drop-shadow(-3px 0 0 #fff) drop-shadow(0 3px 0 #fff) drop-shadow(0 -3px 0 #fff); }
  12% { transform: translate(calc(var(--dx, 0px) * 0.5 + 4px), -12px) scale(1.15) rotate(5deg); }
  16% { transform: translate(calc(var(--dx, 0px) * 0.55 - 4px), -15px) scale(1.28) rotate(-4deg); }
  20% { transform: translate(calc(var(--dx, 0px) * 0.6 + 3px), -18px) scale(1.06) rotate(3deg); }
  25% { transform: translate(calc(var(--dx, 0px) * 0.65 - 2px), -21px) scale(1.12) rotate(-1deg); filter: drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff); }
  34% { transform: translate(calc(var(--dx, 0px) * 0.75), -24px) scale(1.06) rotate(0deg); filter: drop-shadow(0 0 0 #fff) drop-shadow(0 0 0 #fff) drop-shadow(0 0 0 #fff) drop-shadow(0 0 0 #fff); }
  52% { transform: translate(calc(var(--dx, 0px) * 0.85), -26px) scale(1); opacity: 1; }
  100% { transform: translate(var(--dx, 0px), -14px) scale(0.92); opacity: 0; filter: drop-shadow(0 0 0 #fff) drop-shadow(0 0 0 #fff) drop-shadow(0 0 0 #fff) drop-shadow(0 0 0 #fff); }
}

/* ---- Floating words (+xp, a place's name, Lykta): pop in, then drift up ---- */
.float { animation: none; }
.float .blocktext { display: block; margin: 0 auto; height: 18px; animation: fx-float 1.2s cubic-bezier(0.2, 0.8, 0.3, 1) both; }
@keyframes fx-float {
  0% { transform: translateY(6px) scale(0.5); opacity: 0; }
  14% { transform: translateY(-4px) scale(1.12); opacity: 1; }
  26% { transform: translateY(-8px) scale(1); }
  75% { opacity: 1; }
  100% { transform: translateY(-40px); opacity: 0; }
}

/* ---- Spell words glow in their school's colour (violet stays for strikes and travel) ---- */
.label .say.spell.leaf svg.blocktext { filter: drop-shadow(0 0 5px rgba(143, 200, 70, 0.85)); }
.label .say.spell.honey svg.blocktext { filter: drop-shadow(0 0 5px rgba(255, 196, 64, 0.85)); }
.label .say.spell.sky svg.blocktext { filter: drop-shadow(0 0 5px rgba(90, 170, 250, 0.85)); }

/* ---- At eye level your own spell words show before your eyes, just under the cross, as your head is out of sight
   (entities.js eyeSay): the same letters, popping in and glowing in their school's colour ---- */
#eye-say { position: fixed; left: 50%; top: calc(50% + 40px); z-index: 7; transform: translate(-50%, -50%); pointer-events: none; }
#eye-say[hidden] { display: none; }
#eye-say svg.blocktext { display: block; height: 26px; filter: drop-shadow(0 0 6px rgba(150, 120, 255, 0.85)); animation: spellword 0.55s cubic-bezier(.2, 1.5, .5, 1) both; }
#eye-say.leaf svg.blocktext { filter: drop-shadow(0 0 6px rgba(143, 200, 70, 0.85)); }
#eye-say.honey svg.blocktext { filter: drop-shadow(0 0 6px rgba(255, 196, 64, 0.85)); }
#eye-say.sky svg.blocktext { filter: drop-shadow(0 0 6px rgba(90, 170, 250, 0.85)); }

/* ---- Health bars over heads: the lost part stays pale for a moment, then drains ---- */
.label .hpbar { position: relative; }
.label .hpbar div { position: relative; z-index: 1; transition: width 0.1s ease-out; }
.label .hpbar div.trail { position: absolute; left: 0; top: 0; z-index: 0; background: #ffe39a; transition: width 0.4s ease-in 0.3s; }
/* Your target nearly beaten: its bar breathes a soft warm glow (entities.js sets .low). */
.label .hpbar.low { animation: hp-low 0.9s ease-in-out infinite; }
@keyframes hp-low {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 176, 90, 0); transform: scale(1); }
  50% { box-shadow: 0 0 6px 2px rgba(255, 176, 90, 0.85); transform: scale(1.1); }
}
@media (prefers-reduced-motion: reduce) {
  .splat.crit .blocktext { animation: none; filter: drop-shadow(1px 0 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 1px 0 #fff) drop-shadow(0 -1px 0 #fff); }
  .label .hpbar.low { animation: none; box-shadow: 0 0 5px 1px rgba(255, 176, 90, 0.7); }
}

/* Less motion: numbers, words and your level up banner simply appear, and go when their time is up
   (a near-zero animation would have left them at their last, invisible keyframe). */
@media (prefers-reduced-motion: reduce) {
  .splat .blocktext, .float .blocktext, .levelup-banner, .label .say.spell svg.blocktext, #eye-say svg.blocktext { animation: none; }
  .label .hpbar div, .label .hpbar div.trail { transition: none; }
}

/* ---- Progress over your own head: a skill's picture and its experience in honey, what the action gave
   you in cream, rising beside your figure (client/js/progress.js places each row every frame) ---- */
.gain { position: absolute; left: 0; top: 0; pointer-events: none; line-height: 0; z-index: 1; }
.gain-row { display: flex; align-items: center; gap: 9px; transform-origin: 0 60%; animation: gain-rise 1.35s cubic-bezier(0.2, 0.8, 0.3, 1) both; }
.gain-part { display: flex; align-items: center; gap: 2px; }
.gain-icon { display: block; image-rendering: pixelated; }
.gain-part.xp .gain-icon { width: 28px; height: 28px; }
.gain-part.item .gain-icon { width: 18px; height: 18px; }
.gain-num .blocktext { display: block; height: 20px; }
.gain-word { font: 800 13px/1 var(--read); color: #fff6de; text-shadow: var(--inked); white-space: nowrap; }   /* a skill with no picture yet */
.gain-num.bump .blocktext { animation: gain-bump 0.26s ease-out; }
@keyframes gain-rise {
  0% { transform: translateY(10px) scale(0.5); opacity: 0; }
  12% { transform: translateY(-3px) scale(1.12); opacity: 1; }
  24% { transform: translateY(-6px) scale(1); }
  72% { opacity: 1; }
  100% { transform: translateY(-30px); opacity: 0; }
}
@keyframes gain-bump { 0% { transform: scale(1.3); } 100% { transform: scale(1); } }

/* ---- A level up that names what happened: the skill's picture and its new level in block letters,
   and on a parchment ribbon what the level opens ---- */
.levelup-banner.named { display: flex; flex-direction: column; align-items: center; gap: 12px; filter: none; animation-duration: 2.4s; }
.levelup-banner.named.opens { animation-name: banner-named; animation-duration: 3.8s; }
.levelup-banner .lu-title { display: flex; align-items: center; gap: 10px; filter: drop-shadow(0 4px 0 rgba(43, 29, 14, 0.35)) drop-shadow(0 0 18px rgba(255, 214, 90, 0.8)); }
.levelup-banner .lu-title svg.blocktext { display: block; height: 40px; }
.levelup-banner .lu-icon { display: block; width: 42px; height: 42px; image-rendering: pixelated; }
.levelup-banner .lu-open {
  display: flex; align-items: center; gap: 9px; max-width: min(620px, 88vw); padding: 7px 14px 8px 8px;
  background: var(--parchment); border: 3px solid var(--ink); border-radius: 10px; corner-shape: bevel; box-shadow: var(--bevel), var(--ledge);
  font: 700 15px/1.3 var(--read); color: var(--ink); text-align: left; white-space: normal;
}
.levelup-banner .lu-new { flex: none; padding: 0 6px; background: var(--ink); border-radius: 4px; corner-shape: bevel; color: var(--honey); font: 8px/18px var(--blocks); letter-spacing: 0.04em; }
@media (max-width: 560px) {   /* a phone: the title keeps to the screen */
  .levelup-banner .lu-title svg.blocktext { height: 24px; }
  .levelup-banner .lu-icon { width: 28px; height: 28px; }
  .levelup-banner .lu-open { font-size: 13px; }
}
@keyframes banner-named {
  0% { opacity: 0; transform: translateX(-50%) translateY(18px) scale(0.4); }
  9% { opacity: 1; transform: translateX(-50%) translateY(-6px) scale(1.1); }
  15% { transform: translateX(-50%) translateY(0) scale(1); }
  86% { opacity: 1; transform: translateX(-50%) translateY(-4px) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) translateY(-22px) scale(0.96); }
}

/* ---- Danger on monster levels: easy green, even honey, tough orange, too strong red ---- */
.con { display: inline-block; padding: 0 5px; border: 2px solid var(--ink); border-radius: 5px; corner-shape: bevel; font: 800 11px/14px var(--read); color: var(--ink); white-space: nowrap; vertical-align: 1px; text-shadow: none; }
.con.con-0 { background: #8fd07a; }
.con.con-1 { background: #f6c531; }
.con.con-2 { background: #f79a45; }
.con.con-3 { background: #ef6a55; color: #fff6de; }
#menu .opt:hover .con { box-shadow: 0 0 0 1px rgba(255, 246, 222, 0.6); }
.label .name .lv { display: inline-block; margin: 0 -5px 0 1px; padding: 0 4px; border-radius: 5px; corner-shape: bevel; font: 900 10px/13px var(--read); color: #2b1d0e; vertical-align: 0; }
.label .name .lv.con-0 { background: #8fd07a; }
.label .name .lv.con-1 { background: #f6c531; }
.label .name .lv.con-2 { background: #f79a45; }
.label .name .lv.con-3 { background: #ef6a55; color: #fff6de; }
/* The level a skill asks for, in the tooltip: leaf green when you have it, red while you do not. */
#hint .need { color: #3f7a36; font-weight: 800; white-space: nowrap; }
#hint .need.low { color: #c23a26; }
#journey-legend { margin: 8px 0 0; font: 600 12px/1.9 var(--read); color: #4a3a22; }
#journey-legend .con { margin-left: 2px; }

@media (prefers-reduced-motion: reduce) {
  .gain-row, .gain-num.bump .blocktext, .levelup-banner.named, .levelup-banner.named.opens { animation: none; }
}


/* Private work feedback: a small caption in the world, never an announcement card. */
.work-notice{position:absolute;transform:translate(-50%,-100%);width:200px;max-width:calc(100vw - 24px);box-sizing:border-box;padding:5px 8px;border:0;border-radius:4px;background:#243627dc;color:#eee8d7;box-shadow:none;pointer-events:none;font:12px/1.35 system-ui,sans-serif;text-align:center;z-index:9}
.work-notice strong,.work-notice span{display:block}
.work-notice strong{color:#fff5df;margin-bottom:2px;font-size:12px;font-weight:600}
.work-notice span{font-size:11px}
.work-notice.compact{padding:4px 6px}
.work-notice.quiet{width:max-content;padding:3px 9px;background:linear-gradient(90deg,transparent,#20302499 18%,#20302499 82%,transparent);border-radius:0;animation:work-caption-fade 2.4s ease-out both}
.work-notice.quiet strong{margin:0;font-size:12px;font-weight:500;text-shadow:0 1px 2px #152319,0 0 3px #152319}
@keyframes work-caption-fade{0%,65%{opacity:1}100%{opacity:0}}

.work-notice.quiet.compact{background:#233527}

/* A brief sign of recognition, above the name rather than over the creature. */
.monster-notice { position: absolute; pointer-events: none; z-index: 6; animation: monster-notice-fade 1s ease-out both; }
.monster-notice img { display: block; filter: drop-shadow(0 2px 1px #142419); }
@keyframes monster-notice-fade { 0% { opacity: 0; } 12%,70% { opacity: 1; } 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .monster-notice { animation: none; } }
