← back to Model Arena

data/artifacts/1a357c868e55/qwen3-14b.html

328 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>The Empire of Virellis</title>
  <style>
    :root {
      --bg: #0f0f1a;
      --accent: #ff4b4b;
      --text: #fff;
      --border: rgba(255, 255, 255, 0.1);
      --label: #ccc;
      --highlight: #a333ff;
      --event-bg: rgba(255, 255, 255, 0.05);
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }

    body {
      background: var(--bg);
      color: var(--text);
      display: flex;
      flex-direction: column;
      height: 100vh;
      overflow: hidden;
    }

    header {
      background: var(--accent);
      padding: 1rem;
      text-align: center;
      font-size: 1.5rem;
      font-weight: bold;
    }

    main {
      flex: 1;
      display: flex;
      position: relative;
      overflow: hidden;
    }

    .map {
      flex: 1;
      position: relative;
      background: #1a1a2e;
      min-height: 100%;
    }

    .slider-container {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      display: flex;
      align-items: center;
      padding: 0.5rem;
      pointer-events: none;
    }

    .slider {
      flex: 1;
      height: 4px;
      background: var(--border);
      border-radius: 2px;
      cursor: pointer;
      position: relative;
    }

    .slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      appearance: none;
      width: 16px;
      height: 16px;
      background: var(--accent);
      border-radius: 50%;
      cursor: pointer;
      box-shadow: 0 0 4px #ff4b4b;
    }

    .slider::-moz-range-thumb {
      width: 16px;
      height: 16px;
      background: var(--accent);
      border-radius: 50%;
      cursor: pointer;
      box-shadow: 0 0 4px #ff4b4b;
    }

    .slider:focus {
      outline: none;
    }

    .events {
      position: absolute;
      top: 10px;
      left: 10px;
      background: var(--event-bg);
      padding: 0.5rem;
      border: 1px solid rgba(255,255,255,0.1);
      border-radius: 8px;
      max-height: 150px;
      overflow-y: auto;
      z-index: 10;
    }

    .event {
      margin-bottom: 0.3rem;
      color: #fff;
      font-size: 0.9rem;
    }

    .legend {
      position: absolute;
      bottom: 10px;
      right: 10px;
      background: var(--event-bg);
      padding: 0.5rem;
      border: 1px solid rgba(255,255,255,0.1);
      border-radius: 8px;
      max-width: 220px;
      overflow-y: auto;
    }

    .legend-item {
      display: flex;
      align-items: center;
      margin-bottom: 0.3rem;
      font-size: 0.85rem;
    }

    .legend-item span {
      margin-right: 0.5rem;
    }

    .city {
      position: absolute;
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: #ff4b4b;
      transition: transform 0.2s, opacity 0.2s;
    }

    .city.active {
      transform: scale(1.5);
      opacity: 1;
    }

    .border {
      position: absolute;
      pointer-events: none;
      fill: none;
      stroke: var(--border);
      stroke-width: 1;
      transition: stroke 0.3s, stroke-opacity 0.3s;
    }

    .border.active {
      stroke: var(--accent);
      stroke-opacity: 1;
    }

    .event-note {
      position: absolute;
      padding: 0.3rem 0.5rem;
      background: #1a1a2e;
      color: #fff;
      border-radius: 4px;
      font-size: 0.8rem;
      pointer-events: none;
      opacity: 0;
      transition: opacity 0.3s ease;
    }

    .event-note.visible {
      opacity: 1;
    }

    @media (prefers-reduced-motion: no-preference) {
      .city.active {
        animation: pulse 1s ease-in-out;
      }
    }

    @keyframes pulse {
      0%, 100% {
        transform: scale(1);
        opacity: 1;
      }
      50% {
        transform: scale(1.2);
        opacity: 0.8;
      }
    }
  </style>
</head>
<body>
  <header>The Empire of Virellis: 1251–1350</header>
  <main>
    <div class="map" id="map">
      <div class="slider-container">
        <input
          type="range"
          min="1251"
          max="1350"
          value="1251"
          id="slider"
          aria-label="Century Slider"
        />
      </div>
      <svg id="svg-map" width="100%" height="100%" viewBox="0 0 100 100">
        <g id="borders">
          <!-- Example polygons - these would be dynamically generated -->
          <path d="M10 10 L20 20 L30 10 L10 10" class="border" data-year="1255"/>
          <path d="M40 40 L50 50 L60 40 L40 40" class="border" data-year="1300"/>
        </g>
        <g id="cities">
          <!-- Example cities - dynamically generated -->
          <circle cx="20" cy="20" r="4" class="city" data-year="1260"/>
          <circle cx="50" cy="50" r="4" class="city" data-year="1290"/>
        </g>
      </svg>
      <div class="events" id="events">
        <div class="event" id="event-1255">The Pact of Eldros expands Virellis to the north.</div>
        <div class="event" id="event-1280">The Great Collapse: Cities of the Western March fall.</div>
        <div class="event" id="event-1300">Queen Seraphina the Wise unites the southern provinces.</div>
      </div>
      <div class="legend" id="legend">
        <div class="legend-item"><span style="background:#ff4b4b;"></span> Cities</div>
        <div class="legend-item"><span style="background:#a333ff;"></span> Major Events</div>
        <div class="legend-item"><span style="background:#0f0f1a;"></span> Current Map Borders</div>
      </div>
    </div>
  </main>

  <script>
    const slider = document.getElementById("slider");
    const map = document.getElementById("map");
    const svgMap = document.getElementById("svg-map");
    const eventsContainer = document.getElementById("events");
    const legend = document.getElementById("legend");

    const years = [1251, 1255, 1260, 1270, 1280, 1290, 1300, 1310, 1320, 1330, 1340, 1350];

    slider.addEventListener("input", updateMap);
    slider.addEventListener("change", updateMap);

    // Initialize
    updateMap();

    function updateMap() {
      const year = parseInt(slider.value);
      const yearIdx = years.findIndex(y => y === year);
      if (yearIdx === -1) return;

      // Highlight cities
      const cities = document.querySelectorAll(".city");
      cities.forEach(city => {
        const cityYear = parseInt(city.getAttribute("data-year"));
        if (cityYear <= year) {
          city.classList.add("active");
        } else {
          city.classList.remove("active");
        }
      });

      // Border activation
      const borders = document.querySelectorAll(".border");
      borders.forEach(border => {
        const borderYear = parseInt(border.getAttribute("data-year"));
        if (borderYear <= year) {
          border.classList.add("active");
        } else {
          border.classList.remove("active");
        }
      });

      // Event notes
      const eventNotes = document.querySelectorAll(".event-note");
      eventNotes.forEach(note => {
        note.classList.remove("visible");
      });

      // Show event notes based on year
      if (year === 1255) {
        const note1255 = document.getElementById("event-1255");
        if (note1255) {
          note1255.classList.add("visible");
        }
      }

      if (year === 1280) {
        const note1280 = document.getElementById("event-1280");
        if (note1280) {
          note1280.classList.add("visible");
        }
      }

      if (year === 1300) {
        const note1300 = document.getElementById("event-1300");
        if (note1300) {
          note1300.classList.add("visible");
        }
      }
    }

    // Keyboard accessibility
    document.addEventListener("keydown", (e) => {
      if (e.key === "ArrowLeft" && slider.value > 1251) {
        slider.value = parseInt(slider.value) - 10;
        updateMap();
        e.preventDefault();
      } else if (e.key === "ArrowRight" && slider.value < 1350) {
        slider.value = parseInt(slider.value) + 10;
        updateMap();
        e.preventDefault();
      }
    });
  </script>
</body>
</html>