Search

Get Your First Sale!

0 views

Establishing a Deadline that Drives Your First Sale

The moment you close a deal with a publisher or a buyer is a milestone that turns doubt into confidence. But how do you reach that point? The first step is to set a clear, non‑ambiguous deadline that forces the project from an idea into a deliverable. A deadline is not a punitive measure; it is a catalyst that turns a long‑term dream into a concrete target. When I began my journey, I was busy outlining, researching, and drafting. No one was pushing me to turn that into a finished manuscript. It wasn’t until I gave myself a firm cut‑off that the momentum shifted.

Choosing the right deadline means balancing ambition with realism. If you are a seasoned writer who has already written several chapters, a year might be enough to polish your manuscript and create a compelling proposal. If you are still learning core writing skills, you may need a longer timeframe to build the foundation. Think of the deadline as a personal contract: if you miss it, what will you do? In my case, I set a ten‑year target and marked it as the final boundary. That certainty made me stop the endless cycle of “I’ll work on it later” and start acting now.

Keep the deadline tangible. Write it on a calendar, create a visual timeline, or set up recurring reminders on your phone. When you cross off a milestone - such as completing a chapter outline or submitting a query letter - feel the satisfaction. Small wins reinforce the habit of moving forward. Don’t let the deadline be a vague “one day.” Give it a name: “Proposal Submission Day” or “First Pitch Day.” Naming the event creates a psychological anchor that signals to yourself and others that the work is due.

Accountability partners are powerful allies. Share your deadline with a trusted friend, a fellow writer, or a mentor. Tell them you’ll report progress each week. Knowing someone else is tracking your journey pushes you to deliver. If you need an extra layer of commitment, sign a contract with a writing group that includes a small fee to cover meeting costs. The money you pay becomes an investment that keeps you honest.

It’s natural to fear failure when a deadline looms. But remember that the goal is to generate a first sale, not to achieve perfection. A deadline frees you from endless editing. When the clock is ticking, you’ll learn to make the “good enough” decision quickly and move on. And if you do hit the deadline without a sale, treat it as a learning moment. Reset your timeline, refine your pitch, and try again. The discipline you gain from the first deadline will stay with you for every subsequent project.

In short, a well‑chosen deadline turns intention into action. It creates urgency, structures your time, and sets a psychological boundary that fuels persistence. Use it as the launchpad for your first sale.

Crafting and Sending Your Pitch: How to Ask for the Sale

Once the deadline is set, the next step is to actively pursue buyers. “Ask for the sale” isn’t a vague wish; it’s a targeted, deliberate action directed at a specific party with the authority to purchase. Whether you’re selling a novel, a nonfiction manuscript, or a series of articles, the core process remains the same: prepare a compelling proposal, identify the right contact, and send the package with confidence.

For fiction, the standard tool is the partial or proposal. This document typically contains a synopsis, an outline of chapters, and the first 50 to 100 pages of the book. When you draft a partial, keep the language vivid and the plot hooks sharp. Readers and editors skim quickly, so make every sentence count. If you’re writing nonfiction, your proposal might include an executive summary, chapter outlines, and sample chapters, along with a market analysis that shows why your topic matters now.

Choosing the right editor or buyer is crucial. Start by researching publishing houses that specialize in your genre or topic. Look at their submission guidelines, past titles, and the editorial staff’s experience. If you’re pitching to a magazine, find the editors who have published similar pieces. Avoid agents unless you have a strong manuscript that needs representation - agents can’t buy a book; they broker deals on behalf of authors.

When you’ve identified a target, personalize each email. Address the editor by name, reference a recent book or article they published, and explain why your work fits their roster. This shows you’ve done your homework and respect their time. Attach your partial or proposal in the format requested - usually PDF. Keep the subject line clear: “Proposal: – <Your Name>.” In the body, give a brief introduction, a one‑sentence hook, and a call to action: “I would welcome the opportunity to discuss this project further.”</p> <p>Follow‑up is part of the game. If you haven’t heard back after 4–6 weeks, send a polite reminder. Keep it short: “Just checking if you had a chance to review my proposal.” If you receive a rejection, ask for constructive feedback. A thoughtful reply can turn a “no” into a learning experience that improves future submissions.</p> <p>Timing matters. Submissions typically have a “no queries after” date; respect those deadlines. Avoid sending on Mondays or holidays, when editors are overwhelmed. Instead, aim for mid‑week, early in the day. You’ll stand out in a crowded inbox if you’re strategic about when you send your pitch.</p> <p>Finally, keep a log of every submission. Record the date, the editor’s name, the outcome, and any feedback received. This database becomes a roadmap that shows where you’ve found success and where you need to adjust. Treat each proposal as a data point in the pursuit of your first sale.</p><h2>When Rejection Hits: How Feedback Turns a Stalled Pitch into a Winning Sale</h2> <p>Rejection can feel like a wall, but it’s rarely a permanent barrier. The key lies in turning that wall into a stepping stone. When a proposal is turned down, don’t dismiss it as a personal failure. Instead, treat it as an invitation to refine your craft and strategy.</p> <p>Seek feedback from someone who has walked the path you’re on. Find a writer who has sold a book in your genre and ask for a critique of your proposal or manuscript. If you can’t find a mentor in your immediate circle, look for paid services - editing firms, pitching coaches, or literary workshops. Though there’s a cost, the return on investment is often measurable: a revised proposal that lands a meeting, a manuscript polished to a higher standard, or a clearer understanding of market demand.</p> <p>When you receive feedback, dissect it carefully. Identify recurring themes: perhaps the pacing feels slow, the hook isn’t strong enough, or the target market is misaligned. Use a rubric to score each area - plot, character, style, market fit. This structured approach helps you prioritize changes. If multiple reviewers point out the same weakness, it’s a red flag that requires immediate attention.</p> <p>Once you’ve absorbed the insights, draft a revised version. Don’t just patch the problem areas; re‑evaluate the overall structure. Sometimes a complete rewrite of a chapter or a new angle on the premise can make the difference between a “maybe” and a “yes.” If you’re dealing with nonfiction, update your market analysis to reflect recent trends or data. The goal is to show potential buyers that you’ve listened, learned, and evolved.</p> <p>After revision, reset your deadline if needed. A realistic new timeline keeps you accountable without sacrificing momentum. Communicate the new target to your accountability partner or mentor; their support will be invaluable during this second push. When you re‑submit, personalize the cover letter again, and reference the revisions you made - highlight the changes that address the feedback you received.</p> <p>Persistence is the hallmark of every successful author. The industry is saturated; many projects are turned down on the first pass. Those who keep refining, keep learning, and keep pitching are the ones who eventually close a deal. Treat each rejection as data, not a verdict. Adjust your approach, sharpen your proposal, and maintain the discipline that your initial deadline instilled.</p> <p>In the end, the journey to your first sale is a cycle of setting goals, pitching aggressively, learning from setbacks, and refining relentlessly. With a clear deadline, a targeted pitch, and a feedback loop, you’ll move from aspiring writer to confirmed author in a structured, confident manner.</p> </div> <script> (function() { function initCopyableSections() { document.querySelectorAll('.article-content .copyable-section').forEach(function(section) { if (section.querySelector('.copyable-section__btn')) return; var btn = document.createElement('button'); btn.type = 'button'; btn.className = 'copyable-section__btn'; btn.setAttribute('aria-label', 'Copy to clipboard'); var label = section.getAttribute('data-copy-label'); btn.textContent = label ? 'Copy ' + label : 'Copy'; section.appendChild(btn); btn.addEventListener('click', function() { var contentEl = section.querySelector('.copyable-section__content'); var text; if (contentEl) { text = contentEl.textContent.trim(); } else { var clone = section.cloneNode(true); var btnClone = clone.querySelector('.copyable-section__btn'); if (btnClone) btnClone.parentNode.removeChild(btnClone); text = clone.textContent.trim(); } if (!text) return; navigator.clipboard.writeText(text).then(function() { var t = btn.textContent; btn.textContent = 'Copied!'; btn.classList.add('copied'); setTimeout(function() { btn.textContent = t; btn.classList.remove('copied'); }, 2000); }); }); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initCopyableSections); } else { initCopyableSections(); } })(); </script> <!-- Tags --> <div class="article-section-tags sidebar-widget" style="background: var(--github-bg-primary); border: 1px solid var(--github-border); border-radius: 12px; padding: 1.25rem; margin-bottom: 1.5rem;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border);"> <h3 style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); margin: 0; display: flex; align-items: center; gap: 0.5rem; letter-spacing: 0.01em;"> <i class="fas fa-tags" style="color: var(--github-primary); font-size: 0.875rem;"></i> Tags </h3> </div> <div style="display: flex; gap: 0.75rem; flex-wrap: wrap;"> <a href="https://www.murdok.org/news/tag/first-sale" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #first sale </a> <a href="https://www.murdok.org/news/tag/deadline" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #deadline </a> <a href="https://www.murdok.org/news/tag/project-management" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #project management </a> <a href="https://www.murdok.org/news/tag/writing-career" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #writing career </a> <a href="https://www.murdok.org/news/tag/motivation" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #motivation </a> <a href="https://www.murdok.org/news/tag/goal-setting" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #goal setting </a> <a href="https://www.murdok.org/news/tag/publishing" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #publishing </a> <a href="https://www.murdok.org/news/tag/entrepreneurship" style="display: inline-block; padding: 0.5rem 1rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; font-family: 'Inter', sans-serif; font-size: 0.875rem; transition: all 0.2s ease;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)';"> #entrepreneurship </a> </div> </div> <!-- Correction Form --> <div class="article-section-correction sidebar-widget" style="background: var(--github-bg-primary); border: 1px solid var(--github-border); border-radius: 12px; padding: 1.25rem; margin-bottom: 1.5rem;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border);"> <h3 style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); margin: 0; display: flex; align-items: center; gap: 0.5rem; letter-spacing: 0.01em;"> <i class="fas fa-edit" style="color: var(--github-primary); font-size: 0.875rem;"></i> Suggest a Correction </h3> </div> <p style="color: var(--github-text-secondary); font-size: 0.9375rem; margin-bottom: 1.5rem;"> Found an error or have a suggestion? Let us know and we'll review it. </p> <form method="POST" action="https://www.murdok.org/api/submit-correction" id="correctionForm" style="display: flex; flex-direction: column; gap: 1rem;"> <input type="hidden" name="article_type" value="news"> <input type="hidden" name="article_id" value="5068"> <input type="hidden" name="article_url" value="https://www.murdok.org/news/get-your-first-sale"> <div> <label for="correction_name" style="display: block; font-family: 'Inter', sans-serif; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.5rem; font-size: 0.9375rem;"> Your Name <span style="color: #f43f5e;">*</span> </label> <input type="text" name="name" id="correction_name" autocomplete="name" required style="width: 100%; padding: 0.875rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; font-family: 'Inter', sans-serif; font-size: 0.9375rem;" placeholder="Your name"> </div> <div> <label for="correction_email" style="display: block; font-family: 'Inter', sans-serif; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.5rem; font-size: 0.9375rem;"> Your Email <span style="color: #f43f5e;">*</span> </label> <input type="email" name="email" id="correction_email" autocomplete="email" required style="width: 100%; padding: 0.875rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; font-family: 'Inter', sans-serif; font-size: 0.9375rem;" placeholder="your@email.com"> </div> <div> <label for="correction_message" style="display: block; font-family: 'Inter', sans-serif; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.5rem; font-size: 0.9375rem;"> Correction Details <span style="color: #f43f5e;">*</span> </label> <textarea name="message" id="correction_message" autocomplete="off" required rows="5" style="width: 100%; padding: 0.875rem; background: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; font-family: 'Inter', sans-serif; font-size: 0.9375rem; resize: vertical;" placeholder="Describe the error or suggestion..."></textarea> </div> <button type="submit" style="padding: 0.875rem 1.5rem; background: var(--github-primary); color: var(--github-bg-primary); border: none; border-radius: 8px; font-family: 'Inter', sans-serif; font-weight: 600; cursor: pointer; transition: all 0.2s ease; align-self: flex-start;" onmouseover="this.style.opacity='0.9';" onmouseout="this.style.opacity='1';"> <i class="fas fa-paper-plane" style="margin-right: 0.5rem;"></i> Submit Correction </button> </form> <div id="correctionMessage" style="display: none; margin-top: 1rem; padding: 1rem; border-radius: 8px; font-family: 'Inter', sans-serif;"></div> </div> <script> document.getElementById('correctionForm').addEventListener('submit', function(e) { e.preventDefault(); const form = this; const formData = new FormData(form); const messageDiv = document.getElementById('correctionMessage'); fetch('https://www.murdok.org/api/submit-correction', { method: 'POST', body: formData }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { if (data.success) { messageDiv.style.display = 'block'; messageDiv.style.background = 'rgba(34, 197, 94, 0.1)'; messageDiv.style.border = '1px solid var(--github-primary)'; messageDiv.style.color = 'var(--github-primary)'; messageDiv.textContent = 'Thank you! Your correction has been submitted.'; form.reset(); } else { messageDiv.style.display = 'block'; messageDiv.style.background = 'rgba(244, 63, 94, 0.1)'; messageDiv.style.border = '1px solid #f43f5e'; messageDiv.style.color = '#f43f5e'; messageDiv.textContent = data.error || 'An error occurred. Please try again.'; } }) .catch(error => { console.error('Correction submission error:', error); messageDiv.style.display = 'block'; messageDiv.style.background = 'rgba(244, 63, 94, 0.1)'; messageDiv.style.border = '1px solid #f43f5e'; messageDiv.style.color = '#f43f5e'; messageDiv.textContent = 'Network error. Please check your connection and try again.'; }); }); </script> <!-- Social Media Sharing --> <div class="article-section-share sidebar-widget" style="background: var(--github-bg-primary); border: 1px solid var(--github-border); border-radius: 12px; padding: 1.25rem; margin-bottom: 1.5rem;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border);"> <h3 style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); margin: 0; display: flex; align-items: center; gap: 0.5rem; letter-spacing: 0.01em;"> <i class="fas fa-share-alt" style="color: var(--github-primary); font-size: 0.875rem;"></i> Share this article </h3> </div> <div style="display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center;"> <!-- Facebook --> <a href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale" target="_blank" rel="noopener noreferrer" onclick="trackSocialShare('facebook', 5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: #1877F2; color: #fff; border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem;" onmouseover="this.style.transform='translateY(-2px)';" onmouseout="this.style.transform='translateY(0)';" title="Share on Facebook"> <i class="fab fa-facebook-f"></i> </a> <!-- Twitter/X --> <a href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale&text=Get+Your+First+Sale%21" target="_blank" rel="noopener noreferrer" onclick="trackSocialShare('twitter', 5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: #000000; color: #fff; border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem;" onmouseover="this.style.transform='translateY(-2px)';" onmouseout="this.style.transform='translateY(0)';" title="Share on X (Twitter)"> <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: block;"> <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" fill="currentColor"/> </svg> </a> <!-- LinkedIn --> <a href="https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale" target="_blank" rel="noopener noreferrer" onclick="trackSocialShare('linkedin', 5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: #0077B5; color: #fff; border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem;" onmouseover="this.style.transform='translateY(-2px)';" onmouseout="this.style.transform='translateY(0)';" title="Share on LinkedIn"> <i class="fab fa-linkedin-in"></i> </a> <!-- Reddit --> <a href="https://reddit.com/submit?url=https%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale&title=Get+Your+First+Sale%21" target="_blank" rel="noopener noreferrer" onclick="trackSocialShare('reddit', 5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: #FF4500; color: #fff; border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem;" onmouseover="this.style.transform='translateY(-2px)';" onmouseout="this.style.transform='translateY(0)';" title="Share on Reddit"> <i class="fab fa-reddit-alien"></i> </a> <!-- WhatsApp --> <a href="https://wa.me/?text=Get+Your+First+Sale%21%20https%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale" target="_blank" rel="noopener noreferrer" onclick="trackSocialShare('whatsapp', 5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: #25D366; color: #fff; border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem;" onmouseover="this.style.transform='translateY(-2px)';" onmouseout="this.style.transform='translateY(0)';" title="Share on WhatsApp"> <i class="fab fa-whatsapp"></i> </a> <!-- Telegram --> <a href="https://t.me/share/url?url=https%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale&text=Get+Your+First+Sale%21" target="_blank" rel="noopener noreferrer" onclick="trackSocialShare('telegram', 5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: #0088cc; color: #fff; border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem;" onmouseover="this.style.transform='translateY(-2px)';" onmouseout="this.style.transform='translateY(0)';" title="Share on Telegram"> <i class="fab fa-telegram-plane"></i> </a> <!-- Email --> <a href="mailto:?subject=Get+Your+First+Sale%21&body=Establishing+a+Deadline+that+Drives+Your+First+Sale+The+moment+you+close+a+deal+with+a+publisher+or+a+buyer+is+a+milestone+that+turns+doubt+into+confide...%0A%0Ahttps%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale" onclick="trackSocialShare('email', 5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: var(--github-text-secondary); color: #fff; border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem;" onmouseover="this.style.transform='translateY(-2px)';" onmouseout="this.style.transform='translateY(0)';" title="Share via Email"> <i class="fas fa-envelope"></i> </a> <!-- Copy Link --> <button onclick="copyArticleLink(5068);" style="display: inline-flex; align-items: center; justify-content: center; width: 48px; height: 48px; background: var(--github-bg-primary); border: 1px solid var(--github-border); color: var(--github-text-primary); border-radius: 8px; text-decoration: none; transition: all 0.2s ease; font-size: 1.25rem; cursor: pointer; padding: 0;" onmouseover="this.style.borderColor='var(--github-primary)'; this.style.color='var(--github-primary)'; this.style.transform='translateY(-2px)';" onmouseout="this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-primary)'; this.style.transform='translateY(0)';" title="Copy link"> <i class="fas fa-link"></i> </button> </div> </div> <!-- Comments Section --> <div class="article-section-comments" style="margin-top: 3rem; margin-bottom: 1.5rem;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border);"> <h2 style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); margin: 0; display: flex; align-items: center; gap: 0.5rem; letter-spacing: 0.01em;"> <i class="fas fa-comments" style="color: var(--github-primary); font-size: 0.875rem;"></i> Comments (0) </h2> </div> <div style="background: var(--github-bg-secondary); border: 1px solid var(--github-border); border-radius: 8px; padding: 1.25rem; margin-bottom: 1.5rem; text-align: center;"> <p style="color: var(--github-text-secondary); font-family: 'Inter', sans-serif; font-size: 0.9375rem; margin-bottom: 1rem;"> Please <a href="https://www.murdok.org/login?redirect=https%3A%2F%2Fwww.murdok.org%2Fnews%2Fget-your-first-sale" style="color: var(--github-primary); text-decoration: none; font-weight: 600;" onmouseover="this.style.textDecoration='underline';" onmouseout="this.style.textDecoration='none';">sign in</a> to leave a comment. </p> </div> <!-- Comments List --> <div id="commentsList"> <p style="color: var(--github-text-secondary); font-family: 'Inter', sans-serif; font-size: 0.9375rem; text-align: center; padding: 2rem;"> No comments yet. Be the first to comment! </p> </div> </div> <!-- Related Posts --> <div class="article-section-related sidebar-widget" style="background: var(--github-bg-primary); border: 1px solid var(--github-border); border-radius: 12px; padding: 1.25rem; margin-bottom: 1.5rem;"> <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.25rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border);"> <h2 style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); margin: 0; display: flex; align-items: center; gap: 0.5rem; letter-spacing: 0.01em;"> <i class="fas fa-newspaper" style="color: var(--github-primary); font-size: 0.875rem;"></i> Related Articles </h2> </div> <ul style="list-style: none; padding: 0; margin: 0;"> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/google-s-brandy-update-exposed" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <img src="https://www.murdok.org/assets/images/posts/default_post_8379_1770312387_6984d2c325264_medium.jpg" alt="Google's Brandy Update Exposed" width="240" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"> </div> <div> <h3 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> Google's Brandy Update Exposed </h3> <div style="display: flex; align-items: center; gap: 1rem; font-size: 0.75rem; color: var(--github-text-muted);"> <span style="display: inline-flex; align-items: center; gap: 0.375rem;"><i class="fas fa-calendar" style="font-size: 0.6875rem;"></i> Feb 17, 2004</span> </div> </div> </a> </li> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/penetration-test-my-meanest-hack" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <img src="https://www.murdok.org/assets/images/posts/default_post_8325_1770312382_6984d2bedbd0b_medium.jpg" alt="Penetration Test : My Meanest Hack" width="240" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"> </div> <div> <h3 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> Penetration Test : My Meanest Hack </h3> <div style="display: flex; align-items: center; gap: 1rem; font-size: 0.75rem; color: var(--github-text-muted);"> <span style="display: inline-flex; align-items: center; gap: 0.375rem;"><i class="fas fa-calendar" style="font-size: 0.6875rem;"></i> Feb 10, 2004</span> </div> </div> </a> </li> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/developing-a-hit-counter-in-asp-net" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <img src="https://www.murdok.org/assets/images/posts/default_post_8207_1770312373_6984d2b583062_medium.jpg" alt="Developing a Hit Counter in ASP.NET " width="240" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"> </div> <div> <h3 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> Developing a Hit Counter in ASP.NET </h3> <div style="display: flex; align-items: center; gap: 1rem; font-size: 0.75rem; color: var(--github-text-muted);"> <span style="display: inline-flex; align-items: center; gap: 0.375rem;"><i class="fas fa-calendar" style="font-size: 0.6875rem;"></i> Jan 26, 2004</span> </div> </div> </a> </li> </ul> </div> </article> </div> <!-- Right Sidebar - Widgets --> <div class="col-lg-3 right-sidebar-column" style="max-width: 360px; flex: 0 0 360px; min-height: 100px; padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0; margin-top: 0; margin-bottom: 0; flex-shrink: 0;"> <div class="right-sidebar-wrapper" style="position: sticky; top: 2rem; width: 360px; padding-top: 0; padding-bottom: 1rem; padding-left: 0.75rem; padding-right: 0.75rem; margin-top: 0; margin-bottom: 0; box-sizing: border-box;"> <div class="sidebar-widget sidebar-promo-widget sidebar-promo-has-fallback" style="background: var(--github-bg-primary); border: 1px solid var(--github-border); border-radius: 12px; padding: 1.5rem 1.25rem; margin-bottom: 1.5rem; display: flex; align-items: center; justify-content: center;"> <script type="text/template" id="sidebar-adsense-tpl-sidebar-promo-6a047f8feec06"><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4745231616627689"rn crossorigin="anonymous"><\/script>rn<!-- Murdok - Sidebar -->rn<ins class="adsbygoogle"rn style="display:inline-block;width:300px;height:250px"rn data-ad-client="ca-pub-4745231616627689"rn data-ad-slot="2420735192"></ins>rn<script>rn (adsbygoogle = window.adsbygoogle || []).push({});rn<\/script></script> <div id="sidebar-promo-content-sidebar-promo-6a047f8feec06" class="sidebar-adsense-slot sidebar-ad-slot" style="text-align: center; width: 300px; min-width: 300px; min-height: 250px; margin: 0 auto;" data-adsense-tpl="sidebar-adsense-tpl-sidebar-promo-6a047f8feec06"></div> <div class="sidebar-fallback-slot sidebar-ad-slot" style="text-align: center; width: 300px; min-width: 300px; min-height: 250px; margin: 0 auto;"><div style="width: 300px; height: 250px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); display: flex; flex-direction: column; align-items: center; justify-content: center; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);"> <a href="https://murdok.org/contact" style="color: white; text-decoration: none; text-align: center; padding: 20px;"> <div style="font-size: 24px; font-weight: bold; margin-bottom: 15px;">📢</div> <div style="font-size: 20px; font-weight: bold; margin-bottom: 10px;">Advertise Here</div> <div style="font-size: 14px; opacity: 0.9;">Reach thousands of engaged readers</div> <div style="margin-top: 15px; background: white; color: #667eea; padding: 10px 20px; border-radius: 4px; font-weight: 600;">Contact Us</div> </a></div></div> </div> <div class="sidebar-widget cms-latest-news-widget" style="background: var(--github-bg-primary); border: 1px solid var(--github-border); border-radius: 12px; padding: 1.25rem; margin-bottom: 1.5rem; "> <h3 class="widget-title" style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 1.25rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); display: flex; align-items: center; gap: 0.5rem; letter-spacing: 0.01em;"> <i class="fas fa-blog" style="color: var(--github-primary); font-size: 0.875rem;"></i> Latest News </h3> <ul style="list-style: none; padding: 0; margin: 0;"> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/creative-poetry-prompts-specifying-meter-image-and-volta" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <picture><img src="https://www.murdok.org/assets/images/posts/creative-poetry-prompts-specifying-meter-image-and-volta-featured-grok-imagine-image-quality.jpg" alt="Creative Poetry Prompts Specifying Meter, Image, and Volta" width="120" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"></picture> </div> <div> <h4 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> Creative Poetry Prompts Specifying Meter, Image, and Volta </h4> <div style="font-size: 0.75rem; color: var(--github-text-muted);"> <i class="fas fa-calendar-alt" style="font-size: 0.6875rem; margin-right: 0.375rem;"></i>May 13, 2026 </div> </div> </a> </li> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/iterative-prompts-for-turning-messy-outlines-into-dynamic-scenes" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <picture><img src="https://www.murdok.org/assets/images/posts/iterative-prompts-for-turning-messy-outlines-into-dynamic-scenes-featured-grok-imagine-image-quality.jpg" alt="Iterative Prompts for Turning Messy Outlines into Dynamic Scenes" width="120" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"></picture> </div> <div> <h4 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> Iterative Prompts for Turning Messy Outlines into Dynamic Scenes </h4> <div style="font-size: 0.75rem; color: var(--github-text-muted);"> <i class="fas fa-calendar-alt" style="font-size: 0.6875rem; margin-right: 0.375rem;"></i>May 12, 2026 </div> </div> </a> </li> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/ai-powered-character-questionnaires-that-feel-truly-specific" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <picture><img src="https://www.murdok.org/assets/images/posts/ai-powered-character-questionnaires-that-feel-truly-specific-featured-grok-imagine-image-quality.jpg" alt="AI-Powered Character Questionnaires That Feel Truly Specific" width="120" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"></picture> </div> <div> <h4 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> AI-Powered Character Questionnaires That Feel Truly Specific </h4> <div style="font-size: 0.75rem; color: var(--github-text-muted);"> <i class="fas fa-calendar-alt" style="font-size: 0.6875rem; margin-right: 0.375rem;"></i>May 12, 2026 </div> </div> </a> </li> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/crafting-vivid-setting-details-with-constrained-prompts" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <picture><img src="https://www.murdok.org/assets/images/posts/crafting-vivid-setting-details-with-constrained-prompts-featured-grok-imagine-image-quality.jpg" alt="Crafting Vivid Setting Details with Constrained Prompts" width="120" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"></picture> </div> <div> <h4 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> Crafting Vivid Setting Details with Constrained Prompts </h4> <div style="font-size: 0.75rem; color: var(--github-text-muted);"> <i class="fas fa-calendar-alt" style="font-size: 0.6875rem; margin-right: 0.375rem;"></i>May 12, 2026 </div> </div> </a> </li> <li style="margin-bottom: 0.75rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--github-border); transition: all 0.2s ease;" onmouseover="this.style.borderBottomColor='var(--github-border-hover)';" onmouseout="this.style.borderBottomColor='var(--github-border)';"> <a href="https://www.murdok.org/news/how-to-ask-an-llm-for-scene-pacing-feedback-as-a-writer" style="display: block; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.8';" onmouseout="this.style.opacity='1';"> <div style="width: 100%; height: 120px; border-radius: 6px; overflow: hidden; background: var(--github-bg-secondary); margin-bottom: 0.75rem; position: relative; display: flex; align-items: center; justify-content: center;"> <picture><img src="https://www.murdok.org/assets/images/posts/how-to-ask-an-llm-for-scene-pacing-feedback-as-a-writer-featured-grok-imagine-image-quality.jpg" alt="How to Ask an LLM for Scene Pacing Feedback as a Writer" width="120" height="120" style="width: 100%; height: 100%; object-fit: cover; display: block;" loading="lazy" decoding="async"></picture> </div> <div> <h4 style="font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600; color: var(--github-text-primary); margin-bottom: 0.375rem; line-height: 1.4; transition: color 0.2s ease;" onmouseover="this.style.color='var(--github-primary)';" onmouseout="this.style.color='var(--github-text-primary)';"> How to Ask an LLM for Scene Pacing Feedback as a Writer </h4> <div style="font-size: 0.75rem; color: var(--github-text-muted);"> <i class="fas fa-calendar-alt" style="font-size: 0.6875rem; margin-right: 0.375rem;"></i>May 11, 2026 </div> </div> </a> </li> </ul> </div> </div> </div> </div> </div> <style> /* Article sections - match sidebar widget styling exactly */ .article-section-tags.sidebar-widget, .article-section-correction.sidebar-widget, .article-section-share.sidebar-widget, .article-section-comments.sidebar-widget, .article-section-related.sidebar-widget { background: var(--github-bg-primary) !important; border: 1px solid var(--github-border) !important; border-radius: 12px !important; padding: 1.25rem !important; margin-bottom: 1.5rem !important; } [data-bs-theme="dark"] .article-section-tags.sidebar-widget, [data-theme="dark"] .article-section-tags.sidebar-widget, [data-bs-theme="dark"] .article-section-correction.sidebar-widget, [data-theme="dark"] .article-section-correction.sidebar-widget, [data-bs-theme="dark"] .article-section-share.sidebar-widget, [data-theme="dark"] .article-section-share.sidebar-widget, [data-bs-theme="dark"] .article-section-comments.sidebar-widget, [data-theme="dark"] .article-section-comments.sidebar-widget, [data-bs-theme="dark"] .article-section-related.sidebar-widget, [data-theme="dark"] .article-section-related.sidebar-widget { background: #000 !important; } /* Content inside sections should match sidebar container background in dark mode */ [data-bs-theme="dark"] .article-section-tags.sidebar-widget > *, [data-theme="dark"] .article-section-tags.sidebar-widget > *, [data-bs-theme="dark"] .article-section-correction.sidebar-widget > *, [data-theme="dark"] .article-section-correction.sidebar-widget > *, [data-bs-theme="dark"] .article-section-share.sidebar-widget > *, [data-theme="dark"] .article-section-share.sidebar-widget > *, [data-bs-theme="dark"] .article-section-comments.sidebar-widget > *, [data-theme="dark"] .article-section-comments.sidebar-widget > *, [data-bs-theme="dark"] .article-section-related.sidebar-widget > *, [data-theme="dark"] .article-section-related.sidebar-widget > * { background: transparent !important; } /* Form inputs and internal containers should match sidebar background */ [data-bs-theme="dark"] .article-section-correction.sidebar-widget input, [data-theme="dark"] .article-section-correction.sidebar-widget input, [data-bs-theme="dark"] .article-section-correction.sidebar-widget textarea, [data-theme="dark"] .article-section-correction.sidebar-widget textarea, [data-bs-theme="dark"] .article-section-comments.sidebar-widget input, [data-theme="dark"] .article-section-comments.sidebar-widget input, [data-bs-theme="dark"] .article-section-comments.sidebar-widget textarea, [data-theme="dark"] .article-section-comments.sidebar-widget textarea, [data-bs-theme="dark"] .article-section-correction.sidebar-widget .comment-form-container, [data-theme="dark"] .article-section-correction.sidebar-widget .comment-form-container { background: #000 !important; } /* Internal divs with backgrounds should match sidebar */ [data-bs-theme="dark"] .article-section-comments.sidebar-widget .comment-form-container, [data-theme="dark"] .article-section-comments.sidebar-widget .comment-form-container, [data-bs-theme="dark"] .article-section-comments.sidebar-widget > div[style*="background"], [data-theme="dark"] .article-section-comments.sidebar-widget > div[style*="background"], [data-bs-theme="dark"] .article-section-related.sidebar-widget ul li div[style*="background"], [data-theme="dark"] .article-section-related.sidebar-widget ul li div[style*="background"], [data-bs-theme="dark"] .article-section-tags.sidebar-widget a[style*="background"], [data-theme="dark"] .article-section-tags.sidebar-widget a[style*="background"] { background: #000 !important; } /* Ensure all content containers match sidebar background */ [data-bs-theme="dark"] .article-section-tags.sidebar-widget > div, [data-theme="dark"] .article-section-tags.sidebar-widget > div, [data-bs-theme="dark"] .article-section-correction.sidebar-widget > div, [data-theme="dark"] .article-section-correction.sidebar-widget > div, [data-bs-theme="dark"] .article-section-share.sidebar-widget > div, [data-theme="dark"] .article-section-share.sidebar-widget > div, [data-bs-theme="dark"] .article-section-comments.sidebar-widget > div, [data-theme="dark"] .article-section-comments.sidebar-widget > div, [data-bs-theme="dark"] .article-section-related.sidebar-widget > div, [data-theme="dark"] .article-section-related.sidebar-widget > div { background: transparent !important; } /* Ensure proper flex layout for news single page - match news listing page */ .news-main-content { flex: 1 1 0 !important; min-width: 0 !important; max-width: none !important; width: auto !important; padding-left: 1.5rem !important; padding-right: 1.5rem !important; } /* Desktop layout - ensure proper flex behavior */ @media (min-width: 992px) { .row.g-4 { display: flex !important; flex-wrap: nowrap !important; align-items: flex-start !important; } .left-sidebar-column, .right-sidebar-column { flex: 0 0 auto !important; max-width: 360px !important; min-width: 0 !important; } .news-main-content { flex: 1 1 0 !important; min-width: 900px !important; /* Comfortable reading width for articles - matches typical news page width */ max-width: none !important; width: auto !important; padding-left: 1.5rem !important; padding-right: 1.5rem !important; } } /* On mobile, allow wrapping */ @media (max-width: 991px) { .row.g-4 { flex-wrap: wrap !important; } .left-sidebar-column, .right-sidebar-column { width: 100% !important; max-width: 100% !important; flex: 0 0 100% !important; } .news-main-content { width: 100% !important; max-width: 100% !important; flex: 0 0 100% !important; min-width: 0 !important; padding-left: 0 !important; padding-right: 0 !important; } } </style> <script> // Dynamically adjust main content column width based on sidebars document.addEventListener('DOMContentLoaded', function() { const mainContent = document.getElementById('news-main-content'); if (!mainContent) return; const hasLeftSidebar = document.querySelector('.left-sidebar-column') !== null; const hasRightSidebar = document.querySelector('.right-sidebar-column') !== null; const row = mainContent.closest('.row'); // Main content should always flex to fill remaining space mainContent.style.flex = '1 1 0'; mainContent.style.minWidth = '0'; // On desktop (>= 992px), prevent wrapping when sidebars are present function updateLayout() { if (window.innerWidth >= 992) { if (hasLeftSidebar || hasRightSidebar) { if (row) { row.style.flexWrap = 'nowrap'; } } } else { // On mobile, allow wrapping if (row) { row.style.flexWrap = 'wrap'; } } } // Update on load and resize updateLayout(); window.addEventListener('resize', updateLayout); }); </script> <script> // Handle hash links on page load (for any internal links) document.addEventListener('DOMContentLoaded', function() { // Add scroll offset for fixed headers const headings = document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]'); headings.forEach(heading => { heading.style.scrollMarginTop = '100px'; heading.style.scrollPaddingTop = '100px'; }); // Handle hash links on page load if (window.location.hash) { setTimeout(function() { const targetId = window.location.hash.substring(1); const targetElement = document.getElementById(targetId); if (targetElement) { const headerOffset = 100; const elementPosition = targetElement.getBoundingClientRect().top; const offsetPosition = elementPosition + window.pageYOffset - headerOffset; window.scrollTo({ top: offsetPosition, behavior: 'smooth' }); } }, 100); } }); // Track social media shares function trackSocialShare(platform, postId) { fetch('https://www.murdok.org/api/news-share', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ post_id: postId, platform: platform }) }).catch(err => console.log('Share tracking error', err)); } // Copy article link to clipboard function copyArticleLink(postId) { const url = window.location.href; if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(url).then(() => { showCopyNotification('Link copied to clipboard!'); trackSocialShare('copy', postId); }).catch(err => { fallbackCopyText(url, postId); }); } else { fallbackCopyText(url, postId); } } function fallbackCopyText(text, postId) { const input = document.createElement('input'); input.value = text; input.style.position = 'fixed'; input.style.opacity = '0'; document.body.appendChild(input); input.select(); input.setSelectionRange(0, 99999); // For mobile devices try { document.execCommand('copy'); showCopyNotification('Link copied to clipboard!'); if (postId) { trackSocialShare('copy', postId); } } catch (err) { showCopyNotification('Failed to copy link. Please copy manually: ' + text); } document.body.removeChild(input); } function showCopyNotification(message) { // Create notification element const notification = document.createElement('div'); notification.textContent = message; notification.style.cssText = 'position: fixed; bottom: 2rem; right: 2rem; background: var(--github-primary); color: var(--github-bg-primary); padding: 1rem 1.5rem; border-radius: 8px; z-index: 10000; font-family: "Inter", sans-serif; font-weight: 600; font-size: 0.9375rem; animation: slideIn 0.3s ease;'; // Add animation const style = document.createElement('style'); style.textContent = '@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }'; document.head.appendChild(style); document.body.appendChild(notification); // Remove after 3 seconds setTimeout(() => { notification.style.animation = 'slideIn 0.3s ease reverse'; setTimeout(() => { if (document.body.contains(notification)) { document.body.removeChild(notification); } if (document.head.contains(style)) { document.head.removeChild(style); } }, 300); }, 3000); } // Comments functionality document.getElementById('commentForm')?.addEventListener('submit', function(e) { e.preventDefault(); const form = this; const formData = new FormData(form); const messageDiv = document.getElementById('commentMessage'); fetch('https://www.murdok.org/api/article-comment', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { messageDiv.style.display = 'block'; messageDiv.style.background = 'rgba(34, 197, 94, 0.1)'; messageDiv.style.border = '1px solid var(--github-primary)'; messageDiv.style.color = 'var(--github-primary)'; messageDiv.textContent = 'Comment posted successfully!'; form.reset(); document.getElementById('parent_comment_id').value = ''; document.getElementById('cancelReplyBtn').style.display = 'none'; // Reload page to show new comment setTimeout(() => window.location.reload(), 1000); } else { messageDiv.style.display = 'block'; messageDiv.style.background = 'rgba(244, 63, 94, 0.1)'; messageDiv.style.border = '1px solid #f43f5e'; messageDiv.style.color = '#f43f5e'; messageDiv.textContent = data.error || 'An error occurred. Please try again.'; } }) .catch(error => { console.error('Comment submission error:', error); messageDiv.style.display = 'block'; messageDiv.style.background = 'rgba(244, 63, 94, 0.1)'; messageDiv.style.border = '1px solid #f43f5e'; messageDiv.style.color = '#f43f5e'; messageDiv.textContent = 'Network error. Please check your connection and try again.'; }); }); function replyToComment(commentId, commenterName) { document.getElementById('parent_comment_id').value = commentId; document.getElementById('comment_text').placeholder = 'Reply to ' + commenterName + '...'; document.getElementById('comment_text').focus(); document.getElementById('cancelReplyBtn').style.display = 'inline-block'; document.getElementById('comment_text').scrollIntoView({ behavior: 'smooth', block: 'center' }); } function cancelReply() { document.getElementById('parent_comment_id').value = ''; document.getElementById('comment_text').placeholder = 'Write your comment here...'; document.getElementById('cancelReplyBtn').style.display = 'none'; } function deleteComment(commentId) { if (!confirm('Are you sure you want to delete this comment?')) { return; } const formData = new FormData(); formData.append('action', 'delete'); formData.append('comment_id', commentId); fetch('https://www.murdok.org/api/article-comment', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { if (data.success) { window.location.reload(); } else { alert(data.error || 'Failed to delete comment'); } }) .catch(error => { console.error('Delete comment error:', error); alert('Network error. Please try again.'); }); } // Favorite button functionality const favoriteBtn = document.getElementById('favoriteBtn'); if (favoriteBtn) { favoriteBtn.addEventListener('click', function() { const postId = this.dataset.postId; const isFavorited = this.dataset.isFavorited === '1'; // Check if user is logged in // Show signup modal showSignupModal(); return; // Toggle favorite const formData = new FormData(); formData.append('action', 'toggle'); formData.append('post_id', postId); // Disable button during request this.disabled = true; const originalText = this.querySelector('.favorite-text').textContent; this.querySelector('.favorite-text').textContent = '...'; fetch('https://www.murdok.org/api/favorite', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { this.disabled = false; if (data.success) { // Toggle state const newIsFavorited = !isFavorited; this.dataset.isFavorited = newIsFavorited ? '1' : '0'; // Update button appearance const heartIcon = this.querySelector('i.fa-heart'); if (newIsFavorited) { this.style.background = '#dc3545'; this.style.borderColor = '#dc3545'; this.style.color = 'white'; if (heartIcon) heartIcon.style.color = 'white'; this.querySelector('.favorite-text').textContent = 'Favorited'; } else { this.style.background = 'transparent'; this.style.borderColor = '#dc3545'; this.style.color = '#f87171'; if (heartIcon) heartIcon.style.color = '#f87171'; const favoriteText = this.querySelector('.favorite-text'); if (favoriteText) { favoriteText.textContent = 'Favorite'; favoriteText.style.color = '#f87171'; } } // Show toast notification if (typeof GitHubToast !== 'undefined') { GitHubToast.success(newIsFavorited ? 'Article added to favorites' : 'Article removed from favorites'); } // Refresh favorites count in sidebar if widget exists if (typeof refreshFavoritesWidget === 'function') { refreshFavoritesWidget(); } } else { this.querySelector('.favorite-text').textContent = originalText; if (typeof GitHubToast !== 'undefined') { GitHubToast.error(data.error || 'Failed to update favorite'); } else { alert(data.error || 'Failed to update favorite'); } } }) .catch(error => { this.disabled = false; this.querySelector('.favorite-text').textContent = originalText; console.error('Favorite error:', error); if (typeof GitHubToast !== 'undefined') { GitHubToast.error('Network error. Please try again.'); } else { alert('Network error. Please try again.'); } }); }); } // Signup modal function function showSignupModal() { // Create modal if it doesn't exist let modal = document.getElementById('signupModal'); if (!modal) { modal = document.createElement('div'); modal.id = 'signupModal'; modal.className = 'modal fade'; modal.setAttribute('tabindex', '-1'); modal.innerHTML = ` <div class="modal-dialog modal-dialog-centered"> <div class="modal-content" style="background: var(--github-bg-primary); border: 1px solid var(--github-border); border-radius: 12px;"> <div class="modal-header" style="border-bottom: 1px solid var(--github-border); padding: 1.5rem;"> <h5 class="modal-title" style="font-family: 'Inter', sans-serif; font-size: 1.5rem; font-weight: 700; color: var(--github-text-primary);"> <i class="fas fa-user-plus" style="color: var(--github-primary); margin-right: 0.5rem;"></i> Sign Up to Favorite Articles </h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" style="opacity: 1; background-color: var(--github-text-secondary); border-radius: 4px; padding: 0.5rem; cursor: pointer; filter: invert(1);" title="Close"></button> </div> <div class="modal-body" style="padding: 2rem;"> <p style="color: var(--github-text-secondary); font-family: 'Inter', sans-serif; margin-bottom: 1.5rem;"> Create an account to save your favorite articles and access them anytime. </p> <form method="POST" action="https://www.murdok.org/signup" id="modalSignupForm"> <input type="hidden" name="recaptcha_token" id="modal_recaptcha_token"> <div style="margin-bottom: 1rem;"> <label style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); display: block; margin-bottom: 0.5rem;"> Username <span style="color: #ef4444;">*</span> </label> <input type="text" name="username" required pattern="[a-zA-Z0-9_\-]{3,20}" style="width: 100%; background: var(--github-bg-secondary); border: 1px solid var(--github-border); border-radius: 8px; color: var(--github-text-primary); font-family: 'Inter', sans-serif; font-size: 0.9375rem; padding: 0.75rem 1rem;" onfocus="this.style.borderColor='var(--github-primary)';" onblur="this.style.borderColor='var(--github-border)';"> </div> <div style="margin-bottom: 1rem;"> <label style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); display: block; margin-bottom: 0.5rem;"> Email <span style="color: #ef4444;">*</span> </label> <input type="email" name="email" required style="width: 100%; background: var(--github-bg-secondary); border: 1px solid var(--github-border); border-radius: 8px; color: var(--github-text-primary); font-family: 'Inter', sans-serif; font-size: 0.9375rem; padding: 0.75rem 1rem;" onfocus="this.style.borderColor='var(--github-primary)';" onblur="this.style.borderColor='var(--github-border)';"> </div> <div style="margin-bottom: 1.5rem;"> <label style="font-family: 'Inter', sans-serif; font-size: 0.9375rem; font-weight: 600; color: var(--github-text-primary); display: block; margin-bottom: 0.5rem;"> Password <span style="color: #ef4444;">*</span> </label> <input type="password" name="password" required minlength="6" style="width: 100%; background: var(--github-bg-secondary); border: 1px solid var(--github-border); border-radius: 8px; color: var(--github-text-primary); font-family: 'Inter', sans-serif; font-size: 0.9375rem; padding: 0.75rem 1rem;" onfocus="this.style.borderColor='var(--github-primary)';" onblur="this.style.borderColor='var(--github-border)';"> </div> <button type="submit" style="width: 100%; background: var(--github-primary); color: #000000; border: 1px solid var(--github-primary); border-radius: 8px; padding: 0.875rem 1.5rem; font-family: 'Inter', sans-serif; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.2s ease;" onmouseover="this.style.opacity='0.9';" onmouseout="this.style.opacity='1';"> <i class="fas fa-user-plus" style="margin-right: 0.5rem;"></i> Create Account </button> </form> <div style="text-align: center; margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--github-border);"> <p style="margin: 0; color: var(--github-text-secondary); font-size: 0.9375rem; font-family: 'Inter', sans-serif;"> Already have an account? <a href="https://www.murdok.org/login" style="color: var(--github-primary); text-decoration: none; font-weight: 600;" onmouseover="this.style.textDecoration='underline';" onmouseout="this.style.textDecoration='none';"> Sign In </a> </p> </div> </div> </div> </div> `; document.body.appendChild(modal); // Handle reCAPTCHA for modal form (function() { var recaptchaSiteKey = String("6LdJ01IsAAAAAHyuodz2yefPjZa8q9CoeQB0l_xE" || ''); if (recaptchaSiteKey === '') return; var modalForm = document.getElementById('modalSignupForm'); if (modalForm) { modalForm.addEventListener('submit', function(e) { e.preventDefault(); if (typeof grecaptcha !== 'undefined' && grecaptcha.ready) { grecaptcha.ready(function() { grecaptcha.execute(recaptchaSiteKey, { action: String('signup') }) .then(function(token) { document.getElementById('modal_recaptcha_token').value = token; modalForm.submit(); }) .catch(function(err) { document.getElementById('modal_recaptcha_token').value = ''; modalForm.submit(); }); }); } else { modalForm.submit(); } }); } })(); } // Show modal - check if Bootstrap is loaded if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { try { const bsModal = new bootstrap.Modal(modal); bsModal.show(); } catch (e) { console.error('Bootstrap Modal error:', e); // Fallback: show modal manually modal.style.display = 'block'; modal.classList.add('show'); document.body.classList.add('modal-open'); const backdrop = document.createElement('div'); backdrop.className = 'modal-backdrop fade show'; document.body.appendChild(backdrop); // Close button functionality for fallback const closeBtn = modal.querySelector('.btn-close'); if (closeBtn) { closeBtn.onclick = () => { modal.style.display = 'none'; modal.classList.remove('show'); document.body.classList.remove('modal-open'); backdrop.remove(); }; } // Backdrop click to close backdrop.onclick = () => { modal.style.display = 'none'; modal.classList.remove('show'); document.body.classList.remove('modal-open'); backdrop.remove(); }; // ESC key to close const escHandler = (e) => { if (e.key === 'Escape' && modal.classList.contains('show')) { modal.style.display = 'none'; modal.classList.remove('show'); document.body.classList.remove('modal-open'); backdrop.remove(); document.removeEventListener('keydown', escHandler); } }; document.addEventListener('keydown', escHandler); document.body.appendChild(backdrop); } } else { // Fallback: show modal manually modal.style.display = 'block'; modal.classList.add('show'); document.body.classList.add('modal-open'); const backdrop = document.createElement('div'); backdrop.className = 'modal-backdrop fade show'; document.body.appendChild(backdrop); // Close button functionality for fallback const closeBtn = modal.querySelector('.btn-close'); if (closeBtn) { closeBtn.onclick = () => { modal.style.display = 'none'; modal.classList.remove('show'); document.body.classList.remove('modal-open'); backdrop.remove(); }; } // Backdrop click to close backdrop.onclick = () => { modal.style.display = 'none'; modal.classList.remove('show'); document.body.classList.remove('modal-open'); backdrop.remove(); }; // ESC key to close const escHandler = (e) => { if (e.key === 'Escape' && modal.classList.contains('show')) { modal.style.display = 'none'; modal.classList.remove('show'); document.body.classList.remove('modal-open'); backdrop.remove(); document.removeEventListener('keydown', escHandler); } }; document.addEventListener('keydown', escHandler); } } </script> </main> <!-- Footer --> <footer class="border-top mt-5" style="background-color: var(--github-bg-secondary); border-color: var(--github-border) !important;"> <div class="container py-4"> <div class="row g-4"> <!-- Logo --> <div class="col-lg-3 col-md-4"> <div class="mb-3"> <div class="d-flex align-items-center mb-2"> <img src="/assets/images/uploads/logo_dark_1768218367.png" alt="Murdok" width="120" height="32" class="footer-logo-dark" style="max-width: 120px; object-fit: contain;"> </div> <p style="font-family: 'Inter', sans-serif; font-size: 0.875rem; color: var(--github-text-secondary); margin: 0; line-height: 1.5;"> Creative Writing With Artificial Intelligence </p> </div> </div> <!-- Navigation Links --> <div class="col-lg-6 col-md-4"> <div class="row g-4"> <div class="col-6"> <h3 class="fw-bold mb-3" style="color: var(--github-text-primary); text-transform: uppercase; font-size: 0.75rem; letter-spacing: 0.5px; margin: 0 0 1rem 0;"> Navigation </h3> <ul class="list-unstyled mb-0"> <li class="mb-2"><a href="/" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-home" style="width: 16px; text-align: center;"></i> Home</a></li> <li class="mb-2"><a href="/news" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-newspaper" style="width: 16px; text-align: center;"></i> News</a></li> <li class="mb-2"><a href="/wiki" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-book" style="width: 16px; text-align: center;"></i> Wiki</a></li> <li class="mb-2"><a href="/search" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-search" style="width: 16px; text-align: center;"></i> Search</a></li> </ul> </div> <div class="col-6"> <h3 class="fw-bold mb-3" style="color: var(--github-text-primary); text-transform: uppercase; font-size: 12px; letter-spacing: 0.5px; font-size: 0.75rem; margin: 0 0 1rem 0;"> Support </h3> <ul class="list-unstyled mb-0"> <li class="mb-2"><a href="/contact" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-envelope" style="width: 16px; text-align: center;"></i> Contact</a></li> <li class="mb-2"><a href="/privacy" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-shield-alt" style="width: 16px; text-align: center;"></i> Privacy</a></li> <li class="mb-2"><a href="/terms" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-file-contract" style="width: 16px; text-align: center;"></i> Terms</a></li> <li class="mb-2"><a href="/sitemap" class="text-decoration-none fw-medium d-flex align-items-center gap-2" style="color: var(--github-text-secondary); font-size: 14px; transition: color 0.2s ease;"><i class="fas fa-sitemap" style="width: 16px; text-align: center;"></i> Sitemap</a></li> </ul> </div> </div> </div> <!-- Copyright, Social Media, and Theme Toggle --> <div class="col-lg-3 col-md-4"> <div class="d-flex flex-column align-items-md-end"> <div class="d-flex align-items-center gap-2 mb-3 flex-wrap justify-content-md-end"> <a href="https://x.com/MurdokActual" target="_blank" rel="noopener noreferrer" class="text-decoration-none d-flex align-items-center justify-content-center" style="width: 36px; height: 36px; border-radius: 50%; background-color: var(--github-bg-secondary); border: 1px solid var(--github-border); color: var(--github-text-secondary); transition: all 0.2s ease;" onmouseover="this.style.backgroundColor='#000000'; this.style.borderColor='#000000'; this.style.color='white'; this.style.transform='translateY(-2px)';" onmouseout="this.style.backgroundColor='var(--github-bg-secondary)'; this.style.borderColor='var(--github-border)'; this.style.color='var(--github-text-secondary)'; this.style.transform='translateY(0)';" title="Twitter"> <i class="fab fa-x-twitter" style="font-size: 16px;"></i> </a> </div> <p class="mb-3 fw-medium text-md-end" style="color: var(--github-text-secondary); font-size: 13px;"> © 2026 Murdok. All rights reserved. </p> </div> </div> </div> </div> </footer> <!-- Bootstrap 5.3.3 JS Bundle (admin loads in header to avoid bootstrap undefined on sidebar-widgets) --> <script src="/assets/js/bootstrap.bundle.min.js" defer></script> <!-- jQuery 3.7.1 --> <script src="/assets/js/jquery.min.js" defer></script> <!-- Alpine.js --> <script src="/assets/js/alpine.min.js" defer></script> <!-- Cropper.js (only load on profile page or admin pages) - defer for Core Web Vitals --> <!-- Footer Styles --> <style> /* Footer link hover effects */ footer a { transition: color 0.2s ease, transform 0.2s ease; } footer a:hover { color: var(--github-primary) !important; transform: translateX(2px); } /* Logo hover effect */ footer img:hover { transform: scale(1.05); transition: transform 0.2s ease; } footer button:hover { background-color: var(--github-bg-secondary) !important; transform: scale(1.05); } </style> <!-- GitHub-inspired Utility Functions --> <script> // Global image error handler - provides fallback for missing images function handleImageError(img) { // Create a placeholder SVG const placeholder = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgZmlsbD0iI2Y2ZjhmYSIvPjx0ZXh0IHg9IjUwJSIgeT0iNTAlIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMTQiIGZpbGw9IiM2NTZkNzYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIj5JbWFnZSBub3QgYXZhaWxhYmxlPC90ZXh0Pjwvc3ZnPg=='; // Replace with placeholder if not already using it if (img.src !== placeholder) { img.onerror = null; // Prevent infinite loop img.src = placeholder; img.style.backgroundColor = 'var(--github-bg-secondary, #f6f8fa)'; img.style.display = 'block'; } else { // If placeholder also fails, hide the image img.style.display = 'none'; } } // Ensure dark theme is set (site is dark-only) document.addEventListener('DOMContentLoaded', function() { document.documentElement.setAttribute('data-bs-theme', 'dark'); }); // GitHub-inspired Toast System class GitHubToast { static show(message, type = 'info', duration = 5000) { // Ensure toast container exists let toastContainer = document.getElementById('toastContainer'); if (!toastContainer) { toastContainer = document.createElement('div'); toastContainer.id = 'toastContainer'; toastContainer.className = 'toast-container'; toastContainer.style.cssText = 'position: fixed; top: 20px; right: 20px; z-index: 9999; max-width: 400px;'; document.body.appendChild(toastContainer); } const toastId = 'toast-' + Date.now(); const bgClass = type === 'success' ? 'bg-success' : type === 'error' ? 'bg-danger' : type === 'warning' ? 'bg-warning' : 'bg-primary'; const iconClass = type === 'success' ? 'fa-check-circle' : type === 'error' ? 'fa-exclamation-triangle' : type === 'warning' ? 'fa-exclamation-circle' : 'fa-info-circle'; const toastHTML = ` <div id="${toastId}" class="toast align-items-center text-white ${bgClass} border-0" role="alert" aria-live="assertive" aria-atomic="true" style="display: block; opacity: 0; transition: opacity 0.3s ease;"> <div class="d-flex"> <div class="toast-body"> <i class="fas ${iconClass} me-2"></i> ${this.escapeHtml(message)} </div> <button type="button" class="btn-close btn-close-white me-2 m-auto" onclick="document.getElementById('${toastId}').remove()" aria-label="Close"></button> </div> </div> `; toastContainer.insertAdjacentHTML('beforeend', toastHTML); const toastElement = document.getElementById(toastId); // Show toast with animation setTimeout(() => { toastElement.style.opacity = '1'; }, 10); // Use Bootstrap Toast if available, otherwise use fallback if (typeof bootstrap !== 'undefined' && bootstrap.Toast) { const toast = new bootstrap.Toast(toastElement, { delay: duration }); toast.show(); toastElement.addEventListener('hidden.bs.toast', () => { toastElement.remove(); }); } else { // Fallback: simple show/hide toastElement.style.opacity = '1'; // Auto-dismiss if (duration > 0) { setTimeout(() => { toastElement.style.opacity = '0'; setTimeout(() => { if (toastElement.parentNode) { toastElement.remove(); } }, 300); }, duration); } } } static escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } static success(message, duration = 5000) { this.show(message, 'success', duration); } static error(message, duration = 5000) { this.show(message, 'error', duration); } static warning(message, duration = 5000) { this.show(message, 'warning', duration); } static info(message, duration = 5000) { this.show(message, 'info', duration); } } // GitHub-inspired Modal System class GitHubModal { static show(options = {}) { const { title = '', body = '', footer = '', size = 'md', backdrop = true, keyboard = true } = options; const modalId = 'github-modal-' + Date.now(); const sizeClass = size === 'lg' ? 'modal-lg' : size === 'sm' ? 'modal-sm' : ''; const modalHTML = ` <div class="modal fade" id="${modalId}" tabindex="-1" ${backdrop ? '' : 'data-bs-backdrop="static"'} ${keyboard ? '' : 'data-bs-keyboard="false"'}> <div class="modal-dialog ${sizeClass}"> <div class="modal-content"> ${title ? `<div class="modal-header"> <h5 class="modal-title">${title}</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div>` : ''} ${body ? `<div class="modal-body">${body}</div>` : ''} ${footer ? `<div class="modal-footer">${footer}</div>` : ''} </div> </div> </div> `; document.body.insertAdjacentHTML('beforeend', modalHTML); const modalElement = document.getElementById(modalId); // Use Bootstrap Modal if available, otherwise use vanilla JS let modal; if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { modal = new bootstrap.Modal(modalElement); modalElement.addEventListener('hidden.bs.modal', () => { modalElement.remove(); }); modal.show(); } else { // Fallback: show modal manually modalElement.style.display = 'block'; modalElement.classList.add('show'); document.body.classList.add('modal-open'); const backdrop = document.createElement('div'); backdrop.className = 'modal-backdrop fade show'; backdrop.onclick = () => { modalElement.style.display = 'none'; modalElement.classList.remove('show'); document.body.classList.remove('modal-open'); backdrop.remove(); modalElement.remove(); }; document.body.appendChild(backdrop); // Store backdrop reference for cleanup modalElement._backdrop = backdrop; // Close on ESC key const escHandler = (e) => { if (e.key === 'Escape' && modalElement.classList.contains('show')) { backdrop.click(); document.removeEventListener('keydown', escHandler); } }; document.addEventListener('keydown', escHandler); // Add hide method for compatibility modal = { hide: () => { backdrop.click(); }, _element: modalElement }; } return { modal, element: modalElement }; } static confirm(message, title = 'Confirm', confirmText = 'OK', cancelText = 'Cancel') { return new Promise((resolve) => { let settled = false; const finish = (value) => { if (settled) { return; } settled = true; resolve(value); }; const footer = ` <button type="button" class="btn btn-secondary github-modal-btn-cancel" data-bs-dismiss="modal">${cancelText}</button> <button type="button" class="btn btn-primary github-modal-btn-ok">${confirmText}</button> `; const { modal, element: modalElement } = this.show({ title, body: `<p class="mb-0">${message}</p>`, footer }); const confirmBtn = modalElement ? modalElement.querySelector('.github-modal-btn-ok') : null; const cancelBtn = modalElement ? modalElement.querySelector('.github-modal-btn-cancel') : null; const hideModal = () => { if (modal && typeof modal.hide === 'function') { modal.hide(); } else if (modalElement) { modalElement.style.display = 'none'; modalElement.classList.remove('show'); document.body.classList.remove('modal-open'); const back = modalElement._backdrop || document.querySelector('.modal-backdrop'); if (back) { back.remove(); } modalElement.remove(); } }; if (confirmBtn) { confirmBtn.addEventListener('click', () => { finish(true); hideModal(); }); } if (cancelBtn) { cancelBtn.addEventListener('click', () => { finish(false); hideModal(); }); } // Dismiss via X, Escape, or backdrop — only if not already confirmed/cancelled if (modalElement && typeof bootstrap !== 'undefined' && bootstrap.Modal) { modalElement.addEventListener('hidden.bs.modal', () => { finish(false); }); } else if (modalElement) { const backdrop = modalElement._backdrop || document.querySelector('.modal-backdrop'); if (backdrop) { backdrop.addEventListener('click', () => finish(false)); } } }); } } // GitHub-inspired Table Initializers class GitHubTables { static initTabulator(selector, columns, options = {}) { const defaultOptions = { layout: "fitColumns", responsiveLayout: "collapse", pagination: "local", paginationSize: 25, paginationSizeSelector: [10, 25, 50, 100], movableColumns: true, resizableRows: true, placeholder: "No data available", height: "auto" }; return new Tabulator(selector, { columns, ...defaultOptions, ...options }); } } // GitHub-inspired Form Enhancements class GitHubForms { static initSelect2(selector, options = {}) { const defaultOptions = { theme: 'bootstrap-5', width: '100%', placeholder: 'Select an option...', allowClear: true }; return $(selector).select2({ ...defaultOptions, ...options }); } static initSimpleBar(selector) { return new SimpleBar(document.querySelector(selector)); } static initCharts() { // Initialize any Chart.js instances on the page const chartCanvases = document.querySelectorAll('canvas[data-chart]'); chartCanvases.forEach(canvas => { const chartType = canvas.dataset.chart; const chartData = JSON.parse(canvas.dataset.chartData || '{}'); new Chart(canvas, { type: chartType, data: chartData, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: chartType === 'line' || chartType === 'bar' ? { y: { beginAtZero: true, grid: { color: 'rgba(0,0,0,0.1)' } }, x: { grid: { display: false } } } : {} } }); }); } } // Initialize everything when DOM is ready document.addEventListener('DOMContentLoaded', function() { // Add loading states to forms document.querySelectorAll('form').forEach(form => { form.addEventListener('submit', function(e) { const submitBtn = form.querySelector('button[type="submit"], .github-btn-primary'); if (submitBtn && !submitBtn.classList.contains('no-loading')) { submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Loading...'; submitBtn.disabled = true; } }); }); // Add keyboard shortcuts document.addEventListener('keydown', function(e) { // Ctrl/Cmd + K for search focus if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); const searchInput = document.querySelector('input[type="search"], [data-search]'); if (searchInput) { searchInput.focus(); } } // Escape to close modals if (e.key === 'Escape') { if (typeof bootstrap !== 'undefined' && bootstrap.Modal) { const modals = document.querySelectorAll('.modal.show'); modals.forEach(modal => { const bsModal = bootstrap.Modal.getInstance(modal); if (bsModal) { bsModal.hide(); } }); } } }); }); // Make classes globally available window.GitHubToast = GitHubToast; window.GitHubModal = GitHubModal; window.GitHubTables = GitHubTables; window.GitHubForms = GitHubForms; </script> <!-- Responsive Header Search Toggle --> <script> document.addEventListener('DOMContentLoaded', function() { const searchToggle = document.getElementById('headerSearchToggle'); const searchForm = document.getElementById('headerSearchForm'); const searchInput = document.getElementById('headerSearchInput'); if (searchToggle && searchForm) { // Toggle search form on icon click searchToggle.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); searchForm.classList.toggle('expanded'); if (searchForm.classList.contains('expanded')) { searchInput.focus(); } }); // Close search form when clicking outside document.addEventListener('click', function(e) { if (!searchForm.contains(e.target) && !searchToggle.contains(e.target)) { searchForm.classList.remove('expanded'); } }); // Close search form on escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && searchForm.classList.contains('expanded')) { searchForm.classList.remove('expanded'); } }); // Close search form after form submission on mobile if (searchForm) { searchForm.addEventListener('submit', function() { if (window.innerWidth <= 991) { setTimeout(function() { searchForm.classList.remove('expanded'); }, 100); } }); } } }); // Fix hamburger menu on mobile - ensure Bootstrap collapse works // Wait for Bootstrap to be available (it loads in the footer) function initHamburgerMenu() { // Check if Bootstrap is loaded, if not wait a bit and retry if (typeof bootstrap === 'undefined' || !bootstrap.Collapse) { setTimeout(initHamburgerMenu, 100); return; } // Initialize public navbar collapse const publicNavbar = document.getElementById('publicNavbar'); const publicNavbarToggle = document.getElementById('publicNavbarToggle'); if (publicNavbar && publicNavbarToggle) { // Initialize collapse instance if it doesn't exist let publicCollapse = bootstrap.Collapse.getInstance(publicNavbar); if (!publicCollapse) { publicCollapse = new bootstrap.Collapse(publicNavbar, { toggle: false }); } // Ensure the toggle button works publicNavbarToggle.addEventListener('click', function(e) { if (publicCollapse) { publicCollapse.toggle(); } }); } // Initialize admin navbar collapse const adminNavbar = document.getElementById('adminNavbar'); const adminNavbarToggle = document.querySelector('[data-bs-target="#adminNavbar"]'); if (adminNavbar && adminNavbarToggle) { // Initialize collapse instance if it doesn't exist let adminCollapse = bootstrap.Collapse.getInstance(adminNavbar); if (!adminCollapse) { adminCollapse = new bootstrap.Collapse(adminNavbar, { toggle: false }); } // Ensure the toggle button works adminNavbarToggle.addEventListener('click', function(e) { if (adminCollapse) { adminCollapse.toggle(); } }); } } // Start initialization when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initHamburgerMenu); } else { initHamburgerMenu(); } </script> </body> </html>