Inktober 2025

by Kofod

HTML

<div class="calendar-area" id="calendarArea">
    <img class="title-img" id="titleImg" alt="Title" src="https://i.imgur.com/WJ6qmxl.png" />
    <a href="https://en.virtualpopstar.com/social/forum?category=11&topic=368677"
   target="_blank" rel="noopener"
   class="discussion-link">
  <img src="https://i.imgur.com/7WDF94f.png" alt="Inktober Discussion Thread"
       class="discussion-img">
</a>

  <!-- Shine -->
<div id="discussionShine" class="discussion-shine" aria-hidden="true"></div>



    <aside class="info-box" id="infoBox" aria-label="Information">
      <h2>Inktober 2025</h2>
      <p>Here is the event you all have been waiting for!<br>
        Inktober is here! As you might know BloggerTeam is inactive, which means that the all the teams of VirtualPopstar have stuck their heads together to host Inktober this year!<br><br>To discover the prompts, scratch the doors to reveal what is hidden behind!
      </p>
    </aside>

    <div class="grid-wrap">
      <div class="doors-grid" id="doorsGrid" aria-label="October Calendar"></div>
    </div>
  </div>

  <!-- Modal -->
  <div class="modal-backdrop" id="modalBackdrop" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
    <div class="modal-box" id="modalBox">
      <header class="modal-header">
        <h3 id="modalTitle">Day</h3>
        <button class="modal-close" id="modalClose" aria-label="Close">&times;</button>
      </header>
      <div class="modal-content" id="modalContent"></div>
    </div>
  </div>

CSS

body{
    margin:0;
    background:#0b0b0b;
    display:flex;
    justify-content:center;
    align-items:flex-start;
    font-family: Arial, Helvetica, sans-serif;
    color:#fff;
  }
  .calendar-area{
    position:relative;
    width:960px;
    height:1100px;
    overflow:hidden;
    background:#000 center/cover no-repeat;
    background-image:url('https://i.ibb.co/bgrb516h/eeKLUGo.png');
    border-radius:8px;
    box-shadow:0 10px 30px rgba(0,0,0,.5);
  }

  /* --- Header elements --- */
  .title-img{
    position:absolute;
    top:50px;
    left:80px;
    max-width:450px;
    max-height:200px;
    object-fit:contain;
    user-select:none;
    pointer-events:none;
    filter: drop-shadow(3px 5px 2px #0d3064);
  }
  .discussion-img{
    position:absolute;
    width:98px;
    height:98px;
    top:60px;
    left:310px;
    cursor:pointer;
    transition: transform .15s ease;
    z-index: 20;
    filter: drop-shadow(3px 5px 2px #0d3064)
  }
  .discussion-img:hover{
    transform: scale(1.03);
    filter: drop-shadow(5px 7px 4px #0d3064)
    }
  .info-box{
    position:absolute;
    top:16px;
    right:16px;
    width:425px;
    height:140px;
    padding:14px 16px;
    background:rgba(0,0,0,.6);
    border:1px solid rgba(255,255,255,.15);
    border-radius:10px;
    box-shadow:0 6px 20px rgba(0,0,0,.35);
    overflow:auto;
    z-index: 15;
  }
  .info-box h2{ margin:0 0 6px 0; font-size:18px; }
  .info-box p{ margin:0; font-size:14px; line-height:1.35; }

  /* --- Grid area --- */
  .grid-wrap{
    position:absolute;
    left:16px;
    right:16px;
    top:16px;
    bottom:16px;
    padding-top:180px; /* space for title + info */
  }
  .doors-grid{
    width:auto;
    height:100%;
    display:grid;
    grid-template-columns: repeat(7, 1fr);
    grid-gap:8px;
    grid-auto-rows: 171px;
    justify-content:center;
    align-content:start;
    max-width:900px;
    margin:0 auto; 
 ...

JavaScript

(function($){
  /* ---------- CONFIG ---------- */
  var TARGET_MONTH = 10; 
  var TARGET_YEAR  = 2025; 
  var debugAll     = /[?&]debug=1\b/.test(location.search);

  // ---- Scratch look & feel ----
  var BRUSH_RADIUS    = 15;   
  var HARD_EDGE_BRUSH = true; 
  var FADE_THRESHOLD  = 0.60; 
  var TEXTURE_OPACITY = 0.35; 

/* ---------- TOP-LEVEL IMAGE FALLBACKS ---------- */
  // Replace the second URLs with your mirrors
  var ASSETS = {
    title:      { urls: ['https://i.ibb.co/ycnhm2cC/WJ6qmxl.png', 'https://i.imgur.com/WJ6qmxl.png'] },
    discussion: { urls: ['https://i.ibb.co/VpjsWF9B/ink-button.png', 'https://i.imgur.com/7WDF94f.png'] },
    bg:         { urls: ['https://i.ibb.co/bgrb516h/eeKLUGo.png', 'https://i.imgur.com/eeKLUGo.png'] }
  };

  /* ---------- TIMEZONE: CET/CEST ---------- */
  function getCESTDate(){
    var now = new Date();
    var parts = new Intl.DateTimeFormat('en-GB', {
      timeZone: 'Europe/Copenhagen',
      year: 'numeric', month: 'numeric', day: 'numeric'
    }).formatToParts(now);
    var get = function(type){ return parseInt(parts.find(p => p.type === type).value, 10); };
    return { year: get('year'), month: get('month'), day: get('day') };
  }

  /* ---------- HELPERS ---------- */
  function firstWorkingUrl(urls, timeoutMs, cacheBust){
  urls = (urls || []).filter(Boolean);
  var to = (typeof timeoutMs === 'number') ? timeoutMs : 2500;
  var bust = !!cacheBust;

  function testOnce(u){
    return new Promise(function(resolve){
      var img = new Image();
      var done = false;
      var timer = setTimeout(function(){
        if (done) return;
        done = true;
        // Timeout: consider this URL failed
        resolve(null);
      }, to);

      img.onload = function(){
        if (done) return;
        done = true;
        clearTimeout(timer);
        resolve(u);
      };
      img.onerror = function(){
        if (done) return;
        done = true;
     ...