БЕСТИАРИЙ

Кроме широко известных каждому животных в Тенебрии существуют Твари - существа, чье происхождение так или иначе касается сверхестественных сил.

Существа, доступные для того, чтобы сопровождать вас в приключениях по Тенебрии, имеют указанную цену и, в отдельных случаях, условия вербовки. Если ваш персонаж является не магом речь идет только о приручении. Если же вы маг, то Тварь может стать вашим фамильяром. Оплата проводится в банке после чего питомец будет добавлен на вашу личную страницу.

Для того, чтобы предложить дополнение к бестиарию, пожалуйста обратитесь в тему связи с администрацией.

Внимание! При выборе питомца пожалуйста учитывайте региональные особенности отношения к проявлениям магии. В Валонии и Каррамане любые признаки колдовства воспринимаются как угроза, а потому без надлежащего объяснения могут повлечь за собой серьезные последствия.

Код:
[size=12][b]→ тварь:[/b]
название артефакта(ов)
[b]→ сумма позиций:[/b]
цифра + цифра = итог
[b]→ итого в профиле:[/b]
сколько монет после всех манипуляций[/size]

[html]
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body {
  background: #827f78;
}

:root {
  --bestiary-region-bg: var(--quote-input-bg);
  --bestiary-region-text: #312704;

  --bestiary-danger-low: #31531b;
  --bestiary-danger-medium: #764300;
  --bestiary-danger-high: #882424;
  --bestiary-danger-extra-high: #4b0c0c;
}

/* ── section wrapper ── */
.bestiary-section { margin: 0 auto calc(5 * var(--base-spacing)); width: 1012px; }

/* ── grid: narrower cards ── */
.codex-page__grid {
  grid-template-columns: repeat(auto-fill, minmax(155px, 1fr));
  gap: calc(1.5 * var(--base-spacing));
  margin-bottom: 0;
}

/* ── card: interactive ── */
.codex-page__card { cursor: pointer; }

.codex-page__card.is-active { border-color: rgba(184,160,86,0.6); }

/* ── card-img: cover photo override ── */
.codex-page__card-img {
  height: calc(18.5 * var(--base-spacing));
  display: block;
  overflow: hidden;
  flex-shrink: 0;
}

.codex-page__card-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}

/* ── danger (new) ── */
.bestiary-card__danger {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  display: flex;
  align-items: center;
  gap: calc(0.5 * var(--base-spacing));
}

.bestiary-card__danger::before {
  content: '';
  display: inline-block;
  width: var(--base-spacing);
  height: var(--base-spacing);
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

.bestiary-card__danger--low    { color: var(--bestiary-danger-low); }
.bestiary-card__danger--medium { color: var(--bestiary-danger-medium); }
.bestiary-card__danger--high   { color: var(--bestiary-danger-high); }
.bestiary-card__danger--extreme { color: var(--bestiary-danger-extra-high); }

/* ── regions (new) ── */
.bestiary-card__regions {
  display: flex;
  flex-wrap: wrap;
  gap: calc(0.5 * var(--base-spacing));
  flex-grow: 1;
  align-content: flex-start;
}

.bestiary-card__region {
  font-size: 9px;
  background: var(--bestiary-region-bg);
  border: 1px solid rgba(86,77,40,0.42);
  border-radius: calc(0.5 * var(--base-spacing));
  padding: 0 calc(0.5 * var(--base-spacing));
  color: var(--bestiary-region-text);
}

/* ── mage only ── */
.bestiary-card__mage-only {
  position: relative;
  color: var(--codex-price-color);
  font-size: 11px;
  cursor: default;
  flex-shrink: 0;
  display: flex;
  align-items: center;
}

.bestiary-card__mage-only::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + calc(0.5 * var(--base-spacing)));
  right: 0;
  background: #1a1508;
  color: #b89840;
  font-size: 9px;
  font-weight: 600;
  white-space: nowrap;
  padding: calc(0.5 * var(--base-spacing)) var(--base-spacing);
  border-radius: calc(0.5 * var(--base-spacing));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
  z-index: 10;
}

.bestiary-card__mage-only:hover::after { opacity: 1; }

/* ── card footer: flex to push mage icon right ── */
.codex-page__card-footer {
  display: flex !important;
  align-items: center;
  justify-content: space-between;
}

/* ── detail price row ── */
.bestiary-detail__price-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* ── price unavailable ── */
.codex-page__price--unavailable {
  font-size: 10px;
  color: var(--codex-text-muted);
  font-style: italic;
}

/* ── inline detail panel (new) ── */
.bestiary-detail {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: calc(27.5 * var(--base-spacing)) 1fr;
  background: var(--codex-card-bg);
  border: 1px solid var(--codex-card-border);
  border-radius: var(--base-spacing);
  overflow: hidden;
  position: relative;
  opacity: 0;
  transform: translateY(calc(-1 * var(--base-spacing)));
  transition: opacity 0.22s ease, transform 0.22s ease;
}

.bestiary-detail.is-open {
  opacity: 1;
  transform: translateY(0);
}

.bestiary-detail__close {
  position: absolute;
  top: var(--base-spacing);
  right: calc(2 * var(--base-spacing));
  background: none;
  border: 1px solid var(--codex-card-border);
  color: var(--codex-text-muted);
  width: calc(3 * var(--base-spacing));
  height: calc(3 * var(--base-spacing));
  border-radius: calc(0.5 * var(--base-spacing));
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  flex-shrink: 0;
}

.bestiary-detail__img {
  overflow: hidden;
  background: var(--codex-card-img-bg);
  min-height: calc(32.5 * var(--base-spacing));
}

.bestiary-detail__img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}

.bestiary-detail__body {
  padding: calc(2 * var(--base-spacing)) calc(4.5 * var(--base-spacing)) calc(2 * var(--base-spacing)) calc(2 * var(--base-spacing));
  overflow-y: auto;
  max-height: calc(40 * var(--base-spacing));
  text-align: justify;
}

.bestiary-detail__name {
  font-family: "Throne", Georgia, serif;
  font-size: 15px;
  font-weight: 700;
  color: var(--codex-text-primary);
  letter-spacing: 0.06em;
  line-height: 1.25;
  margin-bottom: var(--base-spacing);
}

.bestiary-detail__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--base-spacing);
  margin-bottom: calc(1.5 * var(--base-spacing));
}

.bestiary-detail__divider {
  height: 1px;
  background: linear-gradient(90deg, var(--codex-text-divider), transparent);
  margin: calc(1.5 * var(--base-spacing)) 0;
}

.bestiary-detail__price {
  font-family: "Throne", Georgia, serif;
  font-size: 12px;
  font-weight: 600;
  color: var(--codex-price-color);
  display: flex;
  align-items: center;
  gap: var(--base-spacing);
}

.bestiary-detail__price--unavailable {
  font-size: 11px;
  color: var(--codex-text-muted);
  font-style: italic;
}

.bestiary-detail__description {
  font-size: 11px;
  color: var(--codex-text-muted);
  line-height: 1.75;
}

.bestiary-detail__fields {
  margin-top: calc(2 * var(--base-spacing));
  display: flex;
  flex-direction: column;
  gap: calc(1.5 * var(--base-spacing));
}

.bestiary-detail__field-label {
  font-family: "Throne", Georgia, serif;
  font-size: .9rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--codex-text-primary);
  margin-bottom: calc(0.5 * var(--base-spacing));
}

.bestiary-detail__field-value {
  font-size: 11px;
  color: var(--codex-text-muted);
  line-height: 1.6;
}
</style>

<div class="codex-page" id="bestiary-root"></div>
<script>
  const renderBestiary = () => {
    const dangerLabels = {
      low: 'низкий',
      medium: 'средний',
      high: 'высокий',
      extreme: 'экстремальный',
    };

    let activeCard = null;

    const findLastInRow = (cardEl, grid) => {
      const cards = [...grid.querySelectorAll('.codex-page__card')];
      const top = cardEl.offsetTop;
      let last = cardEl;
      for (const c of cards) {
        if (Math.abs(c.offsetTop - top) < 5) last = c;
      }
      return last;
    };

    const closeDetail = () => {
      const panel = document.getElementById('bestiary-detail');
      if (panel) panel.remove();
      if (activeCard) { activeCard.classList.remove('is-active'); activeCard = null; }
    };

    const openDetail = (item, cardEl) => {
      const alreadyOpen = activeCard === cardEl && document.getElementById('bestiary-detail');
      closeDetail();
      if (alreadyOpen) return;

      activeCard = cardEl;
      cardEl.classList.add('is-active');

      const grid = cardEl.closest('.codex-page__grid');
      const lastInRow = findLastInRow(cardEl, grid);

      const priceInner = item.price
        ? `<div class="bestiary-detail__price"><i class="fa-solid fa-coins" style="opacity:.75;font-size:10px"></i> ${item.price.toLocaleString('ru-RU')}</div>`
        : `<div class="bestiary-detail__price--unavailable">приобретение невозможно</div>`;
      const priceHtml = `<div class="bestiary-detail__price-row">${priceInner}${item.mageOnly ? `<span class="bestiary-card__mage-only" data-tooltip="только для магов"><i class="fa-solid fa-wand-magic-sparkles"></i></span>` : ''}</div>`;

      const panel = document.createElement('div');
      panel.className = 'bestiary-detail';
      panel.id = 'bestiary-detail';
      panel.innerHTML = `
        <button class="bestiary-detail__close" aria-label="Закрыть">✕</button>
        <div class="bestiary-detail__img" style="background-image: url(${item.image})">
        </div>
        <div class="bestiary-detail__body">
          <div class="bestiary-detail__name">${item.name}</div>
          <div class="bestiary-detail__meta">
            <span class="bestiary-card__danger bestiary-card__danger--${item.dangerLevel}">${dangerLabels[item.dangerLevel]}</span>
            ${[...item.regions].sort((a, b) => a.localeCompare(b, 'ru')).map(r => `<span class="bestiary-card__region">${r}</span>`).join('')}
          </div>
          <div class="bestiary-detail__divider"></div>
          ${priceHtml}
          <div class="bestiary-detail__divider"></div>
          <div class="bestiary-detail__description">${item.description}</div>
          <div class="bestiary-detail__fields">
            <div>
              <div class="bestiary-detail__field-label">Продолжительность жизни</div>
              <div class="bestiary-detail__field-value">${item.lifespan}</div>
            </div>
            <div>
              <div class="bestiary-detail__field-label">Приручаемость</div>
              <div class="bestiary-detail__field-value">${item.tamability}</div>
            </div>
            <div>
              <div class="bestiary-detail__field-label">Практическая польза</div>
              <div class="bestiary-detail__field-value">${item.utility}</div>
            </div>
          </div>
        </div>`;

      lastInRow.after(panel);
      requestAnimationFrame(() => {
        panel.classList.add('is-open');
        panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
      });

      panel.querySelector('.bestiary-detail__close').addEventListener('click', closeDetail);
    };

    const render = () => {
      const root = document.getElementById('bestiary-root');
      root.innerHTML = document.BESTIARY_DATA.sections.map(section => `
        <div class="bestiary-section">
          <div class="codex-page__header">${section.title}</div>
          <div class="codex-page__grid">${[...section.items].sort((a, b) => a.name.localeCompare(b.name, 'ru')).map(item => {
            const priceHtml = item.price
              ? `<div class="codex-page__price"><i class="fa-solid fa-coins codex-page__price-icon" style="font-size:10px"></i> ${item.price.toLocaleString('ru-RU')}</div>`
              : `<div class="codex-page__price--unavailable">недоступно</div>`;
            return `
              <div class="codex-page__card" data-id="${item.id}" tabindex="0" role="button" aria-label="${item.name}">
                <div class="codex-page__card-img" style="background-image: url(${item.image})">
                </div>
                <div class="codex-page__card-body">
                  <div class="codex-page__card-name">${item.name}</div>
                  <div class="bestiary-card__danger bestiary-card__danger--${item.dangerLevel}">${dangerLabels[item.dangerLevel]}</div>
                  <div class="bestiary-card__regions">
                    ${[...item.regions].sort((a, b) => a.localeCompare(b, 'ru')).map(r => `<span class="bestiary-card__region">${r}</span>`).join('')}
                  </div>
                  <div class="codex-page__card-footer">
                    ${priceHtml}
                    ${item.mageOnly ? `<span class="bestiary-card__mage-only" data-tooltip="только для магов"><i class="fa-solid fa-wand-magic-sparkles"></i></span>` : ''}
                  </div>
                </div>
              </div>`;
          }).join('')}</div>
        </div>`).join('');

      const allItems = document.BESTIARY_DATA.sections.flatMap(s => s.items);
      root.querySelectorAll('.codex-page__card').forEach(card => {
        const handler = () => {
          const item = allItems.find(i => i.id === card.dataset.id);
          if (item) openDetail(item, card);
        };
        card.addEventListener('click', handler);
        card.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') handler(); });
      });
    };

    document.addEventListener('keydown', e => { if (e.key === 'Escape') closeDetail(); });

    render();
  };

  const s = document.createElement('script');
  s.src = 'https://forumstatic.ru/files/001c/77/b6/81176.js';
  s.onload = renderBestiary;
  document.head.appendChild(s);
</script>

[/html]
[hideprofile]