Profile
Back to NewsBack
GitHub Trending 10 min
Reader Mode
umutxyp/Seo-Promt-Master: 🔍 Google SEO's full docs as an AI prompt machine — drop it into your AI assistant to map every public route, audit each page against Google's rules, and fix the gaps step by step.

umutxyp/Seo-Promt-Master: 🔍 Google SEO's full docs as an AI prompt machine — drop it into your AI assistant to map every public route, audit each page against Google's rules, and fix the gaps step by step.

14 hours ago

🔍 SEO Prompt Master

Google SEO, every other search engine, and AI answer engines — as an AI skill, a prompt library, and a tool that actually runs.

Install it into your project, and every coding agent you use — Claude Code, Codex, Cursor, Gemini CLI, Copilot — gains an SEO + GEO auditor. It maps your routes, checks them against the official rules of Google, Bing, Yandex, Apple and the AI crawlers, fixes the gaps, and proves the fixes with a real audit against your live server.

License: MIT</a> Version</a> Tests</a> Docs: Google Search Central</a> Works with</a> PRs welcome</a>

Usage guide · Türkçe kullanım rehberi · Prompt library · Knowledge base


⚡ Quick start — pick one

| You want… | Do this | Time | |---|---|---| | A score and a findings list for a live site | npx github:umutxyp/Seo-Promt-Master --url https://your-site.com --md seo-report.md | 1 min | | An agent to audit and fix your codebase | bash /path/to/Seo-Promt-Master/install.sh inside your project, then tell your agent "run the SEO audit" | 1 min to set up | | A ready-made prompt for one task (robots.txt, hreflang, JSON-LD, traffic drop…) | Open the prompt library, copy, fill in {{variables}}, paste | 30 s | | To read the rules | docs/README.md — a current, cited reference to SEO and GEO | — |

No account, no API key, and zero dependencies. Node 18+ is the only requirement for the tool.


What is this?

Four things in one repository:

  1. A knowledge base (docs/01–docs/18), distilled from Google Search Central, web.dev, and the official documentation of Bing, Yandex, Apple, Brave, DuckDuckGo and the AI crawler operators. Every claim is cited. Docs 01–11 cover the page. Docs 12–18 cover crawling and crawl budget, indexing and canonicals, content quality and the spam policies, measurement, migrations, off-page authority, and the engines beyond Google.
  2. A skill that installs into whichever agent you use, so "audit my SEO" starts the whole workflow without you pasting anything.
  3. A prompt library — 47 copy-paste prompts in 8 categories, from "rewrite my robots.txt" to "diagnose a traffic drop". Each one is built to ask for evidence and to refuse outdated advice. It's also available as a CSV.
  4. Two tools that run (tools/). Reading a repository tells you what a project intends; only a request tells you what the server returns.
The tools are not decoration. Tested against production sites before release, seo-audit.mjs found live P1 blockers that source review had missed: a Disallow: /_next/ breaking rendering, and a template returning 200 for every URL under it.

🚀 Install as a skill

git clone https://github.com/umutxyp/Seo-Promt-Master.git
cd /path/to/your-project
bash /path/to/Seo-Promt-Master/install.sh

That copies the knowledge base, prompt library and tools into .seo-prompt-master/, then writes the entry-point file each agent actually reads:

| Agent | File it discovers | |---|---| | Claude Code | .claude/skills/seo-audit/SKILL.md | | OpenAI Codex CLI, Amp, Jules, Windsurf | AGENTS.md | | Cursor | .cursor/rules/seo-prompt-master.mdc | | Gemini CLI | GEMINI.md | | GitHub Copilot | .github/copilot-instructions.md |

Then just ask: "run the SEO audit". The agent finds the workflow itself.

If your project already has an AGENTS.md or GEMINI.md, the installer appends to it and never replaces it. Re-running the installer is safe.

Prefer not to install? Put the repo next to your project — most agents auto-discover AGENTS.md and start on their own. Or paste START.md into any chat assistant and say "begin at Phase 0".


🛠️ The tools

# The thorough pass — sample the sitemap, audit each page, compute SEO + GEO scores
npx github:umutxyp/Seo-Promt-Master --url https://example.com --max 40 --md report.md

(or, from a clone:) node tools/seo-audit.mjs --url https://example.com --max 40 --md report.md

The ten-second deploy tripwire

SEO_SMOKE_404_PATHS="/ /blog /products" bash tools/seo-smoke.sh https://example.com

Both have zero dependencies. The auditor needs Node 18+; the smoke test needs curl and awk. Both exit non-zero on failure, so either can gate a deploy or a CI job (--fail-on P1|P2|never), and both work against http://localhost:3000.

What seo-audit.mjs catches that reading code cannot:

  • Soft 404s, template by template — a route that answers 200 for URLs that don't exist. In frameworks with Suspense boundaries this has no visible symptom at all: a loading.tsx above one segment silently turns that whole template's 404 into a 200 shell.
  • One-way hreflang — a set that isn't reciprocal is discarded in full, so every language in the cluster loses the signal. You can only see it by comparing pages against each other.
  • Signals that contradict each other — a canonical pointing at a redirect, a 404 or a noindex page; sitemap URLs that redirect or that robots.txt blocks; a noindex sent in the X-Robots-Tag header that no one sees in the HTML.
  • A crawler access matrix — robots.txt resolved per RFC 9309 for Googlebot, Bingbot (which also feeds DuckDuckGo, Yahoo and Copilot), Applebot (Siri, Spotlight, Safari), YandexBot, DuckDuckBot, Baiduspider, Naver and Seznam, plus 16 AI crawlers split into training, AI search and user-triggered. Blocking Bing by accident is a P1; blocking an AI trainer is a policy decision.
  • Streamed metadata — frameworks that flush early and emit </code> later. Browsers hoist it; bots that stop reading at <code></head></code> see an untitled page.</li> <li><strong>Bot protection</strong> — it recognises a Cloudflare, Akamai or Imperva challenge and reports it once, instead of scoring an unreadable site as broken.</li> <li>Plus: render-critical JS and CSS blocked by robots.txt, sitemap limits and <code>lastmod</code> credibility (including <code>.xml.gz</code>), host consolidation, duplicate titles, JSON-LD validity and policy (<code>Product</code> with no offer, review or rating; <code>aggregateRating</code> with no ratings behind it), raw-HTML content volume, image dimensions, viewport, <code>lang</code>, and AI-answer snippet controls (<code>nosnippet</code>, <code>noarchive</code>/<code>nocache</code>).</li></ul> <strong>The output</strong> is an SEO Score and a GEO Score out of 100, each with a per-category breakdown that shows where the points went, the crawler access table, and every finding cited to <code>docs/</code>. <p><strong>What it deliberately cannot see</strong> — so it stays your job: content quality (<code>docs/14</code>), off-page authority (<code>docs/17</code>), and real field Core Web Vitals, which come from CrUX rather than from fetching a page (<code>docs/05</code>). A high score means the technical foundation is sound, not that the site will rank.</p> <p>See <a href="tools/README.md" target="_blank" rel="noopener"><code>tools/README.md</code></a> for every flag.</p> <hr> <h2>📚 The prompt library</h2> <p>| Category | Examples | |---|---| | <a href="prompt-library/01-start-here.md" target="_blank" rel="noopener">Start here</a> | Full audit & fix · 15-minute health check · Explain my report | | <a href="prompt-library/02-technical-seo.md" target="_blank" rel="noopener">Technical SEO</a> | robots.txt · sitemap · soft 404s · canonicals · migration redirect map · hreflang · JS rendering · CWV | | <a href="prompt-library/03-on-page-and-content.md" target="_blank" rel="noopener">On-page & content</a> | Titles & descriptions in bulk · internal linking · thin-content triage · E-E-A-T | | <a href="prompt-library/04-structured-data.md" target="_blank" rel="noopener">Structured data</a> | JSON-LD generator · validator · entity + <code>sameAs</code> | | <a href="prompt-library/05-geo-ai-search.md" target="_blank" rel="noopener">GEO: AI answer engines</a> | AI-crawler policy · answer-first rewrite · AI citation tracking | | <a href="prompt-library/06-multi-engine.md" target="_blank" rel="noopener">Beyond Google</a> | Bing + IndexNow · Yandex · Apple/Safari/Brave · Naver/Baidu | | <a href="prompt-library/07-monitoring-and-diagnosis.md" target="_blank" rel="noopener">Monitoring & diagnosis</a> | Traffic drop · Search Console export · CI gates · monthly report | | <a href="prompt-library/08-framework-recipes.md" target="_blank" rel="noopener">Framework recipes</a> | Next.js · Nuxt · SvelteKit/Astro/Remix · WordPress · Laravel/Django/Rails |</p> <hr> <h2>📂 What's inside</h2> <pre><code class="">seo-prompt-master/ ├── install.sh ← writes the skill into your project, for every agent ├── package.json ← <code>npx github:umutxyp/Seo-Promt-Master</code> runs the auditor ├── GUIDE.md · GUIDE.tr.md ← usage guides (English · Türkçe) ├── VERSION · CHANGELOG.md ← semver; a major means old scores aren't comparable │ ├── .claude/skills/seo-audit/SKILL.md ← Claude Code ├── AGENTS.md ← Codex, Amp, Jules, Windsurf (and most others) ├── .cursor/rules/seo-prompt-master.mdc ← Cursor ├── GEMINI.md ← Gemini CLI ├── .github/copilot-instructions.md ← GitHub Copilot ├── CLAUDE.md · START.md ← the workflow itself │ ├── tools/ ← the parts that run │ ├── seo-audit.mjs live SEO + GEO audit, scored, exits 1 on any P1 │ ├── seo-smoke.sh deploy tripwire │ └── build-prompts-csv.mjs regenerates prompt-library/prompts.csv │ ├── prompt-library/ ← 47 copy-paste prompts in 8 categories (+ prompts.csv) ├── prompts/ ← the 5-phase workflow (+ 1 optional) ├── docs/ ← the knowledge base (source of truth), 01–18 ├── verticals/ ← 24 industry overlays (optional, additive) ├── checklists/ · templates/ · examples/ └── tests/ ← fixture sites the tools are tested against (npm test)</code></pre> <hr> <h2>🧠 The workflow</h2> <pre><code class="">START.md │ ├─ Phase 0 Bootstrap ......... detect framework, i18n, rendering; load docs/; │ RUN tools/seo-audit.mjs for the baseline ├─ Phase 1 Discover .......... every route → ROUTES-INVENTORY.md │ classify: public-index / public-noindex / private ├─ Phase 2 Audit ............. 9-point check per page → SEO-AUDIT-PROGRESS.md ├─ Phase 3 Prioritize ........ one backlog, infra-first (P1 → P2 → P3) ├─ Phase 4 Fix & verify ...... change → typecheck/lint/build → re-fetch → tick └─ Phase 5 Live signals ...... optional: CrUX and live scrape via MCP</code></pre> <p>Progress lives in <code>ROUTES-INVENTORY.md</code> and <code>SEO-AUDIT-PROGRESS.md</code>, so a long run survives a context reset and resumes where it stopped.</p> <hr> <h2>✅ What it checks</h2> <p>Metadata · Canonical + hreflang · Robots and indexing (meta <strong>and</strong> header) · Structured data · Headings and semantics · Images · Internal links and pagination · Rendering · Sitemap · <strong>Crawler access for every major search engine and AI crawler</strong> — every rule traces to a cited section in <code>docs/</code>.</p> <p><strong>The output is a number with its working shown:</strong> a deterministic <strong>SEO Score</strong> and <strong>GEO Score</strong> out of 100 (<a href="docs/11-scoring-rubric.md" target="_blank" rel="noopener"><code>docs/11</code></a>), each with a per-category breakdown. A P1 crawl or index blocker caps a page's score no matter what else it gets right, because a page that cannot be indexed gains nothing from polish. No score is reported as final without full coverage plus a self-recheck of a random sample. And it is always stated as a technical-readiness score: backlinks, content quality and competition are out of scope.</p> <hr> <h2>❤️ Why it exists</h2> <p>Most SEO checklists are shallow, generic, or quietly out of date. This one is:</p> <ul><li><strong>Current</strong> (reviewed October 2026). It is explicit about what changed: INP replaced FID in March 2024; FAQ rich results are limited to government and health sites; <code>HowTo</code> rich results are gone; <code>rel=next/prev</code> has been unused by Google since 2019; Google doesn't support IndexNow but Bing and Yandex do; Bing still obeys <code>Crawl-delay</code>; <code>llms.txt</code> is not a ranking or citation lever. Advice that repeats the old version is out of date, and the knowledge base says so.</li> <li><strong>Not Google-only.</strong> Google is the reference engine, but Bing's index sits behind DuckDuckGo, Yahoo and Copilot, and Applebot sits behind Siri and Safari's suggestions. <a href="docs/18-other-search-engines.md" target="_blank" rel="noopener"><code>docs/18</code></a> covers where the engines differ, and the auditor checks all of them.</li> <li><strong>Cited.</strong> Every claim links to official documentation. If something isn't in <code>docs/</code>, the agent is told to say "not covered by the knowledge base" rather than recall it from training data.</li> <li><strong>Executable and tested.</strong> An agent can run it against your code <em>and</em> your server. The tools are themselves tested against a healthy and a deliberately broken fixture site on every push.</li> <li><strong>Honest about its limits.</strong> It separates ranking factors from hygiene, logs deliberate skips, and names what it can't measure.</li></ul> <hr> <h2>👤 Author</h2> <p><strong>Umut Bayraktar</strong> — <a href="https://github.com/umutxyp" target="_blank" rel="noopener">@umutxyp</a></p> <p>Full-stack developer, AI-systems researcher and content creator, with 6+ years of building platforms end to end. Founder and CEO of <strong><a href="https://codeshare.me" target="_blank" rel="noopener">Codeshare Technology</a></strong>, a London-based software studio (founded 2025; its products date back to 2020) built on one idea: <em>software that stays free for the people who need it.</em></p> <p>The methodology here comes from running these products, not from theory — and the tools were tested against their production sites before every release:</p> <p>| Product | What it is | |---|---| | 🎵 <strong><a href="https://beatra.app" target="_blank" rel="noopener">Beatra</a></strong> | Free, high-quality Discord music bot with a full web dashboard — relaunched October 2025 | | 🛡️ <strong><a href="https://sylon.app" target="_blank" rel="noopener">Sylon</a></strong> | AI-powered Discord moderation: spam and ad protection, tickets, server guard, welcome automation, logs and analytics — grown out of <em>Server Support</em> (2020) | | ⛏️ <strong><a href="https://mcstat.org" target="_blank" rel="noopener">MCStat</a></strong> | Live Minecraft discovery layer — 6.7K+ servers with real-time stats and player counts | | 💬 <strong><a href="https://justdiscord.org" target="_blank" rel="noopener">JustDiscord</a></strong> | Directory of Discord servers, bots, emojis, stickers and packs with verified ownership — 8K+ bots, 90K+ emojis, 23 languages | | 📺 <strong><a href="https://justanime.me" target="_blank" rel="noopener">JustAnime</a></strong> | Anime and manga tracker — your library, per-episode and per-chapter progress, ratings and reviews | | 🤖 <strong><a href="https://dotrai.com" target="_blank" rel="noopener">Dotrai</a></strong> | Browser-based AI assistant that answers with live web search and runs real code to check its own work |</p> <p>Across the portfolio: <strong>41.5K+ Discord servers</strong>, <strong>3M+ Discord users</strong> and <strong>466K+ Minecraft players</strong> reached (figures from codeshare.me, October 2026).</p> <p><strong>Open source:</strong> <a href="https://github.com/umutxyp/MusicBot" target="_blank" rel="noopener">MusicBot</a> · <a href="https://github.com/umutxyp/Seo-Promt-Master" target="_blank" rel="noopener">Seo-Promt-Master</a> · <a href="https://github.com/umutxyp/Discord-Bot-Website" target="_blank" rel="noopener">Discord-Bot-Website</a> · <a href="https://github.com/umutxyp/Personal-Website" target="_blank" rel="noopener">Personal-Website</a> · <a href="https://github.com/umutxyp/v14-discord-bot" target="_blank" rel="noopener">v14-discord-bot</a> · <a href="https://github.com/umutxyp/Shroudly" target="_blank" rel="noopener">Shroudly</a></p> <p>🔗 <a href="https://umutbayraktar.vercel.app" target="_blank" rel="noopener">Portfolio</a> · <a href="https://github.com/umutxyp" target="_blank" rel="noopener">GitHub</a> · <a href="https://linkedin.com/in/umutxyp" target="_blank" rel="noopener">LinkedIn</a> · <a href="https://youtube.com/@umutxyp" target="_blank" rel="noopener">YouTube</a> · <a href="https://codeshare.me" target="_blank" rel="noopener">Codeshare Technology</a></p> <p>If this saved you time, <strong>star the repo</strong>. 🌟</p> <hr> <h2>📄 License</h2> <p><a href="LICENSE" target="_blank" rel="noopener">MIT</a> © Umut Bayraktar (<a href="https://github.com/umutxyp" target="_blank" rel="noopener">@umutxyp</a>). The knowledge base is compiled from public documentation by Google Search Central, web.dev, Microsoft Bing, Yandex, Apple, Brave, DuckDuckGo and the AI crawler operators. All trademarks belong to their owners. This project is not affiliated with or endorsed by any of them.</p> <hr> <h2>🤝 Contributing</h2> <p>Search guidance moves. PRs that update a rule with a source link, add a framework recipe or a prompt, or add a check to <code>tools/</code> (with a fixture test) are very welcome — see <a href="CONTRIBUTING.md" target="_blank" rel="noopener">CONTRIBUTING.md</a>.</p> </article> <!-- Article Completion Section --> <div class="reader-footer mt-5 pt-5"> <div class="reader-completion-box rounded-3 p-4 text-center"> <div class="reader-completion-icon mb-3"> <i class="fas fa-check-circle"></i> </div> <p class="reader-completion-text fw-semibold mb-1" style="font-size: 1rem;">You finished the article!</p> <p class="reader-completion-subtext small mb-4">Want to go deeper or explore more stories?</p> <div class="d-flex justify-content-center gap-3 flex-wrap"> <a href="https://github.com/umutxyp/Seo-Promt-Master" target="_blank" rel="noopener noreferrer" class="btn btn-primary-custom btn-sm rounded-pill px-4"> <i class="fas fa-external-link-alt me-2"></i>Full Article on GitHub Trending </a> <a href="https://neshdevtech.com/news" class="reader-back-btn btn btn-sm rounded-pill px-4"> <i class="fas fa-newspaper me-2"></i>More Tech News </a> </div> </div> </div> <!-- Internal Linking CTA --> </div> </div> </div> <!-- Sidebar (desktop only) --> <div class="col-lg-4 col-xl-3 d-none d-lg-block"> <div class="sticky-top" style="top: 80px;"> <!-- Mini Controller Widget --> <div class="card border-0 shadow-sm mb-4 bg-surface"> <div class="card-body p-4"> <h6 class="fw-bold mb-3"><i class="fas fa-glasses me-2 text-primary-custom"></i>Reading Mode</h6> <p class="text-secondary-custom small mb-4">Adjust typography and theme to your preference.</p> <div class="mb-3"> <label class="text-muted-custom small d-block mb-2">Typography</label> <div class="d-flex gap-2"> <button class="btn btn-light btn-sm flex-grow-1 border-custom" id="sidebar-font-serif">Serif</button> <button class="btn btn-light btn-sm flex-grow-1 border-custom" id="sidebar-font-sans">Sans</button> </div> </div> <div class="mb-4"> <label class="text-muted-custom small d-block mb-2">Text Scale</label> <div class="d-flex gap-2"> <button class="btn btn-light btn-sm flex-grow-1 border-custom" id="sidebar-size-dec"><i class="fas fa-minus small"></i></button> <button class="btn btn-light btn-sm flex-grow-1 border-custom" id="sidebar-size-inc"><i class="fas fa-plus small"></i></button> </div> </div> <div class="border-top border-light-custom pt-3"> <a href="https://github.com/umutxyp/Seo-Promt-Master" target="_blank" rel="noopener noreferrer" class="btn btn-outline-primary btn-sm w-100 rounded-pill"> View Original <i class="fas fa-external-link-alt ms-1"></i> </a> </div> </div> </div> <!-- Sidebar Related News --> <div class="card border-0 shadow-sm bg-surface"> <div class="card-header bg-transparent border-0 pt-4 pb-0 px-4"> <h5 class="card-title fw-bold mb-0" style="font-size: 1.05rem;"> <i class="fas fa-newspaper me-2 text-primary-custom"></i>Related Stories </h5> </div> <div class="card-body p-0"> <div class="list-group list-group-flush rounded-0 bg-transparent"> <a href="https://neshdevtech.com/news/show-hn-local-pretrained-classifiers-gpu-not-needed-qjtuD" class="list-group-item list-group-item-action border-0 px-4 py-3 bg-transparent border-bottom border-light-custom hover-bg-light"> <h6 class="mb-1 fw-semibold text-truncate-2" style="font-size: 0.875rem; line-height: 1.4;"> Show HN: Local pretrained classifiers, GPU not needed </h6> <div class="d-flex align-items-center justify-content-between text-muted-custom mt-1" style="font-size: 0.72rem;"> <span>Hacker News</span> <span>10 hours ago</span> </div> </a> <a href="https://neshdevtech.com/news/embedcatalog-is-participating-in-hacktoberfest-2026-XBuJP" class="list-group-item list-group-item-action border-0 px-4 py-3 bg-transparent border-bottom border-light-custom hover-bg-light"> <h6 class="mb-1 fw-semibold text-truncate-2" style="font-size: 0.875rem; line-height: 1.4;"> EmbedCatalog is participating in Hacktoberfest 2026 </h6> <div class="d-flex align-items-center justify-content-between text-muted-custom mt-1" style="font-size: 0.72rem;"> <span>Dev.to</span> <span>11 hours ago</span> </div> </a> <a href="https://neshdevtech.com/news/what-if-ai-worked-at-1000000-tokens-per-seconds-rcsjw" class="list-group-item list-group-item-action border-0 px-4 py-3 bg-transparent border-bottom border-light-custom hover-bg-light"> <h6 class="mb-1 fw-semibold text-truncate-2" style="font-size: 0.875rem; line-height: 1.4;"> What if AI worked at 1.000.000 tokens per seconds? </h6> <div class="d-flex align-items-center justify-content-between text-muted-custom mt-1" style="font-size: 0.72rem;"> <span>Hacker News</span> <span>1 day ago</span> </div> </a> <a href="https://neshdevtech.com/news/grapheneos-has-fixed-the-android-17-qpr1-kernel-performance-regression-WDQQE" class="list-group-item list-group-item-action border-0 px-4 py-3 bg-transparent border-bottom border-light-custom hover-bg-light"> <h6 class="mb-1 fw-semibold text-truncate-2" style="font-size: 0.875rem; line-height: 1.4;"> GrapheneOS has fixed the Android 17 QPR1 kernel performance regression </h6> <div class="d-flex align-items-center justify-content-between text-muted-custom mt-1" style="font-size: 0.72rem;"> <span>Hacker News</span> <span>1 day ago</span> </div> </a> <a href="https://neshdevtech.com/news/stop-fixing-headline-gaps-with-negative-margins-use-text-box-trim-hZEmR" class="list-group-item list-group-item-action border-0 px-4 py-3 bg-transparent border-bottom border-light-custom hover-bg-light"> <h6 class="mb-1 fw-semibold text-truncate-2" style="font-size: 0.875rem; line-height: 1.4;"> Stop Fixing Headline Gaps With Negative Margins. Use `text-box-trim` </h6> <div class="d-flex align-items-center justify-content-between text-muted-custom mt-1" style="font-size: 0.72rem;"> <span>Dev.to</span> <span>2 days ago</span> </div> </a> </div> </div> </div> </div> </div> </div> <!-- Mobile: Related News horizontal scroll strip --> <div class="d-block d-lg-none mt-4"> <h6 class="fw-bold mb-3 px-1"> <i class="fas fa-newspaper me-2 text-primary-custom"></i>Related Stories </h6> <div class="related-scroll-strip"> <a href="https://neshdevtech.com/news/show-hn-local-pretrained-classifiers-gpu-not-needed-qjtuD" class="related-scroll-card"> <p class="related-scroll-title">Show HN: Local pretrained classifiers, GPU not needed</p> <div class="related-scroll-meta"> <span>Hacker News</span> <span>10 hours ago</span> </div> </a> <a href="https://neshdevtech.com/news/embedcatalog-is-participating-in-hacktoberfest-2026-XBuJP" class="related-scroll-card"> <p class="related-scroll-title">EmbedCatalog is participating in Hacktoberfest 2026</p> <div class="related-scroll-meta"> <span>Dev.to</span> <span>11 hours ago</span> </div> </a> <a href="https://neshdevtech.com/news/what-if-ai-worked-at-1000000-tokens-per-seconds-rcsjw" class="related-scroll-card"> <p class="related-scroll-title">What if AI worked at 1.000.000 tokens per seconds?</p> <div class="related-scroll-meta"> <span>Hacker News</span> <span>1 day ago</span> </div> </a> <a href="https://neshdevtech.com/news/grapheneos-has-fixed-the-android-17-qpr1-kernel-performance-regression-WDQQE" class="related-scroll-card"> <p class="related-scroll-title">GrapheneOS has fixed the Android 17 QPR1 kernel performance regression</p> <div class="related-scroll-meta"> <span>Hacker News</span> <span>1 day ago</span> </div> </a> <a href="https://neshdevtech.com/news/stop-fixing-headline-gaps-with-negative-margins-use-text-box-trim-hZEmR" class="related-scroll-card"> <p class="related-scroll-title">Stop Fixing Headline Gaps With Negative Margins. Use `text-box-trim`</p> <div class="related-scroll-meta"> <span>Dev.to</span> <span>2 days ago</span> </div> </a> </div> </div> </div> </section> </main> <!-- Footer --> <!-- Footer --> <footer class="py-5 mt-5 position-relative overflow-hidden" style="background: var(--surface); border-top: 1px solid var(--border); box-shadow: var(--shadow-lg);"> <!-- Decorative subtle background gradients --> <div class="position-absolute" style="top: -200px; right: -200px; width: 400px; height: 400px; background: rgba(var(--primary-rgb), 0.03); filter: blur(100px); border-radius: 50%; pointer-events: none;"></div> <div class="position-absolute" style="bottom: -200px; left: -200px; width: 400px; height: 400px; background: rgba(var(--accent-rgb), 0.03); filter: blur(100px); border-radius: 50%; pointer-events: none;"></div> <div class="container position-relative z-3"> <div class="row g-4"> <!-- Column 1: Brand & About --> <div class="col-lg-4 col-md-6"> <div class="d-flex align-items-center mb-3"> <span class="brand-mark me-2" style="width: 42px; height: 42px;" aria-hidden="true"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg> </span> <span class="fs-4 fw-extrabold text-primary-custom" style="letter-spacing: -0.05em; font-family: 'JetBrains Mono', monospace;">NeshDevTech</span> </div> <p class="text-secondary-custom mb-4 pe-lg-4" style="font-size: 0.95rem; line-height: 1.6;">NeshDevTech is a professional technology firm passionate about creating innovative, high-performance web and mobile solutions.</p> <div class="d-flex gap-2"> </div> </div> <!-- Column 2: Navigation Links --> <div class="col-lg-2 col-md-6 col-6"> <h6 class="text-primary-custom fw-bold text-uppercase mb-3" style="font-size: 0.85rem; letter-spacing: 0.05em;">Quick Navigation</h6> <ul class="list-unstyled mb-0"> <li class="mb-2"><a href="https://neshdevtech.com" class="footer-link text-secondary-custom text-decoration-none d-inline-block">Home</a></li> <li class="mb-2"><a href="https://neshdevtech.com/projects" class="footer-link text-secondary-custom text-decoration-none d-inline-block">Projects</a></li> <li class="mb-2"><a href="https://neshdevtech.com/blog" class="footer-link text-secondary-custom text-decoration-none d-inline-block">Tech Blog</a></li> <li class="mb-2"><a href="https://neshdevtech.com/pricing" class="footer-link text-secondary-custom text-decoration-none d-inline-block">Get Quote</a></li> <li class="mb-2"><a href="https://neshdevtech.com/contact" class="footer-link text-secondary-custom text-decoration-none d-inline-block">Contact Me</a></li> </ul> </div> <!-- Column 3: Tech Stack & Services --> <div class="col-lg-3 col-md-6 col-6"> <h6 class="text-primary-custom fw-bold text-uppercase mb-3" style="font-size: 0.85rem; letter-spacing: 0.05em;">Core Services</h6> <ul class="list-unstyled mb-0"> <li class="text-secondary-custom mb-2 small"><i class="bi bi-rocket-takeoff text-primary-custom me-2"></i>Custom Laravel Apps</li> <li class="text-secondary-custom mb-2 small"><i class="bi bi-laptop text-primary-custom me-2"></i>SPA (React & Vue.js)</li> <li class="text-secondary-custom mb-2 small"><i class="bi bi-phone-vibrate text-primary-custom me-2"></i>Flutter Mobile Apps</li> <li class="text-secondary-custom mb-2 small"><i class="bi bi-search text-primary-custom me-2"></i>SEO & Performance Opt</li> </ul> </div> <!-- Column 4: Contact & Availability --> <div class="col-lg-3 col-md-6"> <h6 class="text-primary-custom fw-bold text-uppercase mb-3" style="font-size: 0.85rem; letter-spacing: 0.05em;">Get in Touch</h6> <ul class="list-unstyled mb-3"> <li class="text-secondary-custom mb-2 small d-flex align-items-center"> <i class="bi bi-geo-alt text-primary-custom me-2 fs-6"></i>Nairobi, Kenya </li> <li class="text-secondary-custom mb-2 small d-flex align-items-center"> <i class="bi bi-envelope-at text-primary-custom me-2 fs-6"></i> <a href="mailto:info@neshdevtech.com" class="text-secondary-custom text-decoration-none footer-link">info@neshdevtech.com</a> </li> </ul> <div class="bg-primary-custom-light border border-primary-custom border-opacity-10 p-3 rounded-3" style="border-radius: 12px !important;"> <small class="d-block fw-semibold text-primary-custom mb-1"><i class="bi bi-patch-check-fill me-1"></i>Available for Projects</small> <p class="mb-0 text-secondary-custom small" style="font-size: 0.8rem; line-height: 1.4;">Need a high-performing system? Let's discuss your project.</p> </div> </div> </div> <hr style="border-color: var(--border); opacity: 0.8; margin: 2.5rem 0 2rem;"> <div class="row align-items-center"> <div class="col-md-6 text-center text-md-start mb-2 mb-md-0"> <p class="mb-0 text-secondary-custom small">© 2026 NeshDevTech. All rights reserved.</p> </div> <div class="col-md-6 text-center text-md-end"> <p class="mb-0 text-secondary-custom small">Built with <i class="bi bi-heart-fill text-danger"></i></p> </div> </div> </div> </footer> <!-- Bootstrap JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <!-- Custom JavaScript --> <script> // Theme Management System class ThemeManager { constructor() { this.themeToggle = document.getElementById('themeToggle'); this.themeIcon = document.getElementById('theme-icon'); this.mobileThemeToggle = document.getElementById('mobileThemeToggle'); this.mobileThemeIcon = document.getElementById('mobile-theme-icon'); this.init(); } init() { // Get current theme (already applied in head) const currentTheme = document.documentElement.getAttribute('data-theme') || 'light'; // Update icons to match current theme this.updateIcon(currentTheme); // Listen for desktop theme toggle clicks if (this.themeToggle) { this.themeToggle.addEventListener('click', (e) => { e.preventDefault(); this.toggleTheme(); }); } // Listen for mobile theme toggle clicks if (this.mobileThemeToggle) { this.mobileThemeToggle.addEventListener('click', (e) => { e.preventDefault(); this.toggleTheme(); }); } // Listen for system theme changes window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { if (!localStorage.getItem('theme')) { this.setTheme(e.matches ? 'dark' : 'light'); } }); } setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); document.documentElement.setAttribute('data-bs-theme', theme); if (theme === 'dark') { document.documentElement.classList.add('dark'); } else { document.documentElement.classList.remove('dark'); } localStorage.setItem('theme', theme); this.updateThemeColor(theme); this.updateIcon(theme); } updateThemeColor(theme) { document.querySelectorAll('meta[name="theme-color"]').forEach((meta) => { meta.setAttribute('content', theme === 'dark' ? '#1c2028' : '#e4e8f0'); }); } toggleTheme() { const currentTheme = document.documentElement.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; this.setTheme(newTheme); // Close mobile menu if open const navbarCollapse = document.querySelector('.navbar-collapse'); if (navbarCollapse && navbarCollapse.classList.contains('show') && window.innerWidth < 992) { setTimeout(() => { const bsCollapse = new bootstrap.Collapse(navbarCollapse, { toggle: false }); bsCollapse.hide(); }, 150); } } updateIcon(theme) { const iconClass = theme === 'dark' ? 'bi bi-sun-fill' : 'bi bi-moon-fill'; // Update desktop icon if (this.themeIcon) { this.themeIcon.className = iconClass; } // Update mobile icon if (this.mobileThemeIcon) { this.mobileThemeIcon.className = iconClass; } } } // Initialize theme manager when DOM is loaded document.addEventListener('DOMContentLoaded', () => { new ThemeManager(); // Initialize navbar live clock (function initNavClock(){ const clockEls = Array.from(document.querySelectorAll('[data-clock]')); if (!clockEls.length) return; const locale = navigator.language || 'en-US'; // Detect 12/24h by locale: use resolvedOptions hourCycle when available const dtf = new Intl.DateTimeFormat(locale, { hour: 'numeric' }); const uses12h = (dtf.resolvedOptions().hour12 === true); const fmt = new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: uses12h }); const update = () => { const now = new Date(); const text = fmt.format(now); clockEls.forEach(el => { el.textContent = text; }); }; update(); setInterval(update, 1000); })(); }); // Smooth scrolling for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Navbar background on scroll window.addEventListener('scroll', () => { const navbar = document.querySelector('.navbar'); if (window.scrollY > 50) { navbar.style.backdropFilter = 'blur(20px)'; navbar.style.backgroundColor = 'rgba(var(--background-rgb), 0.8)'; } else { navbar.style.backdropFilter = 'blur(10px)'; navbar.style.backgroundColor = 'var(--background)'; } }); // Add fade-in animation to elements when they come into view const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate-fade-in-up'); } }); }, observerOptions); // Observe elements with animation class document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('.animate-on-scroll').forEach(el => { observer.observe(el); }); }); // Enhanced mobile menu functionality document.addEventListener('DOMContentLoaded', function() { const navbarToggler = document.querySelector('.navbar-toggler'); const navbarCollapse = document.querySelector('.navbar-collapse'); const navLinks = document.querySelectorAll('.navbar-nav .nav-link'); const socialLinks = document.querySelectorAll('.navbar-nav .d-flex a'); let isToggling = false; // Prevent rapid clicking // Enhanced menu close function function closeMenu() { if (navbarCollapse && navbarCollapse.classList.contains('show')) { // Use Bootstrap's collapse API for smooth transition const bsCollapse = new bootstrap.Collapse(navbarCollapse, { toggle: false }); bsCollapse.hide(); } // Reset hamburger animation and aria-expanded if (navbarToggler) { navbarToggler.classList.remove('active'); navbarToggler.setAttribute('aria-expanded', 'false'); } } // Enhanced hamburger toggle functionality if (navbarToggler) { navbarToggler.addEventListener('click', function(e) { e.preventDefault(); // Prevent rapid clicking if (isToggling) return; isToggling = true; const isMenuOpen = navbarCollapse.classList.contains('show'); if (isMenuOpen) { closeMenu(); } else { // Open menu const bsCollapse = new bootstrap.Collapse(navbarCollapse, { toggle: false }); bsCollapse.show(); navbarToggler.setAttribute('aria-expanded', 'true'); } // Reset toggle flag after animation setTimeout(() => { isToggling = false; }, 350); }); } // Close menu when clicking outside document.addEventListener('click', function(event) { const isClickInsideNav = navbarCollapse.contains(event.target); const isToggler = navbarToggler.contains(event.target); const isMenuOpen = navbarCollapse.classList.contains('show'); if (!isClickInsideNav && !isToggler && isMenuOpen && window.innerWidth < 992) { closeMenu(); } }); // Close menu when clicking on navigation links (mobile) navLinks.forEach(function(link) { link.addEventListener('click', function(e) { const isMenuOpen = navbarCollapse.classList.contains('show'); if (isMenuOpen && window.innerWidth < 992 && !this.classList.contains('dropdown-toggle')) { // Add a small delay to let the link navigation start setTimeout(() => { closeMenu(); }, 100); } }); }); // Close menu when clicking on social links (mobile) socialLinks.forEach(function(link) { link.addEventListener('click', function() { if (window.innerWidth < 992) { setTimeout(() => { closeMenu(); }, 100); } }); }); // Escape key closes menu document.addEventListener('keydown', function(event) { if (event.key === 'Escape' && window.innerWidth < 992) { closeMenu(); } }); // Handle window resize to close menu when switching to desktop window.addEventListener('resize', function() { if (window.innerWidth >= 992) { closeMenu(); } }); // Theme toggle functionality is handled by ThemeManager class // No separate event listener needed here // Prevent menu items from being too small on touch devices if ('ontouchstart' in window) { navLinks.forEach(function(link) { link.style.minHeight = '44px'; link.style.display = 'flex'; link.style.alignItems = 'center'; }); } // Add smooth scrolling for anchor links within pages navLinks.forEach(function(link) { const href = link.getAttribute('href'); if (href && href.startsWith('#')) { link.addEventListener('click', function(e) { e.preventDefault(); const target = document.querySelector(href); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } closeMenu(); }); } }); }); </script> <script> // Sticky navbar scroll enhancement (desktop only) (function () { const navbar = document.querySelector('.navbar'); if (!navbar) return; let ticking = false; const SCROLL_THRESHOLD = 20; // px function updateNavbar() { if (window.innerWidth >= 992) { if (window.scrollY > SCROLL_THRESHOLD) { navbar.classList.add('scrolled'); } else { navbar.classList.remove('scrolled'); } } else { // Always remove on mobile so it doesn't affect mobile look navbar.classList.remove('scrolled'); } ticking = false; } window.addEventListener('scroll', function () { if (!ticking) { requestAnimationFrame(updateNavbar); ticking = true; } }, { passive: true }); window.addEventListener('resize', updateNavbar, { passive: true }); // Run once on page load in case page is already scrolled (e.g. back-navigation) updateNavbar(); })(); </script> <script> // Lightweight toast badge notifications (function(){ function ensureContainer(){ let c = document.getElementById('toast-badge-container'); if(!c){ c = document.createElement('div'); c.id = 'toast-badge-container'; c.setAttribute('aria-live','polite'); c.setAttribute('aria-atomic','true'); document.body.appendChild(c); } return c; } function iconFor(type){ switch(type){ case 'success': return '<i class="bi bi-check-circle"></i>'; case 'warning': return '<i class="bi bi-exclamation-triangle"></i>'; case 'error': return '<i class="bi bi-x-circle"></i>'; default: return '<i class="bi bi-info-circle"></i>'; } } window.notifyBadge = function(message, opts={}){ const { type = 'success', timeout = 2500 } = opts; const container = ensureContainer(); const el = document.createElement('div'); el.className = 'toast-badge ' + type; el.setAttribute('role','status'); el.innerHTML = '<span class="icon">'+iconFor(type)+'</span><span>'+message+'</span>'; container.appendChild(el); requestAnimationFrame(()=>{ el.classList.add('show'); }); const remove = () => { el.classList.remove('show'); setTimeout(()=> el.remove(), 200); }; setTimeout(remove, timeout); return { remove }; }; // Global copy helper used by inline buttons window.copyToClipboard = function(url){ try { navigator.clipboard.writeText(url).then(()=>{ // Update the clicked button, if available let btn = document.activeElement && document.activeElement.tagName === 'BUTTON' ? document.activeElement : (window.event && window.event.target && window.event.target.closest ? window.event.target.closest('button') : null); if (btn){ const original = btn.innerHTML; btn.innerHTML = '<i class="fas fa-check me-1"></i> Copied!'; btn.classList.remove('btn-outline-secondary'); btn.classList.add('btn-success'); setTimeout(()=>{ btn.innerHTML = original; btn.classList.remove('btn-success'); btn.classList.add('btn-outline-secondary'); }, 2000); } notifyBadge('Link copied to clipboard', { type: 'success' }); }).catch((err)=>{ console.error('Clipboard error:', err); notifyBadge('Could not copy link', { type: 'error' }); }); } catch (e) { console.error(e); notifyBadge('Copy not supported', { type: 'error' }); } }; })(); </script> <a href="https://wa.me/254768033943?text=Hi%2C+I+visited+your+website+and+would+like+to+inquire+about+your+services." class="whatsapp-btn-float" target="_blank" rel="noopener noreferrer" aria-label="Chat on WhatsApp"> <i class="fab fa-whatsapp"></i> <span class="whatsapp-tooltip">Chat with me</span> </a> <style> .whatsapp-btn-float { position: fixed; bottom: 24px; right: 24px; z-index: 1050; display: flex; align-items: center; justify-content: center; background-color: #25d366; color: #fff !important; width: 60px; height: 60px; border-radius: 50%; box-shadow: 0 4px 16px rgba(37, 211, 102, 0.45); text-decoration: none; transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .whatsapp-btn-float i { font-size: 32px; transition: transform 0.3s ease; } .whatsapp-btn-float:hover { background-color: #128c7e; transform: scale(1.1); box-shadow: 0 6px 20px rgba(18, 140, 126, 0.6); } .whatsapp-btn-float:hover i { transform: rotate(10deg); } /* Pulsing ring animation */ .whatsapp-btn-float::before { content: ''; position: absolute; width: 100%; height: 100%; border-radius: 50%; background-color: #25d366; opacity: 0.7; z-index: -1; animation: whatsapp-pulse 2s infinite; } @keyframes whatsapp-pulse { 0% { transform: scale(1); opacity: 0.7; } 100% { transform: scale(1.6); opacity: 0; } } /* Tooltip style utilizing site-wide theme colors */ .whatsapp-btn-float .whatsapp-tooltip { position: absolute; right: 76px; background-color: var(--surface); color: var(--text-primary); border: 1px solid var(--border); padding: 8px 14px; border-radius: 20px; font-size: 14px; font-weight: 600; white-space: nowrap; opacity: 0; visibility: hidden; transform: translateX(10px); transition: all 0.3s ease; box-shadow: var(--shadow); pointer-events: none; } .whatsapp-btn-float:hover .whatsapp-tooltip { opacity: 1; visibility: visible; transform: translateX(0); } /* Responsive Design */ @media (max-width: 768px) { .whatsapp-btn-float { bottom: 16px; right: 16px; width: 50px; height: 50px; } .whatsapp-btn-float i { font-size: 26px; } .whatsapp-btn-float .whatsapp-tooltip { display: none; /* Hide tooltip on small screens to prevent overlap issues */ } } </style> <!-- Mobile More Menu (Offcanvas Bottom Sheet) - Auth actions only --> <style> /* ══════════════════════════════════════════════════════════════ NEUMORPHISM REVAMP (front-end only) Soft-extruded UI: same-tone surfaces with dual light/dark shadows. This block sits at the end of the body so it layers over every page's own styles without touching page markup. ══════════════════════════════════════════════════════════════ */ /* ── Buttons: extruded, press-in on click ─────────────────────── */ .btn { box-shadow: var(--shadow) !important; transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1) !important; } .btn:hover { transform: translateY(-2px) !important; box-shadow: var(--shadow-md) !important; } .btn:active:not(.btn-link), .btn.show { transform: translateY(1px) scale(0.99) !important; box-shadow: var(--shadow-inset) !important; } .btn-primary-custom, .btn-primary, .btn-success { background: var(--primary-color) !important; border-color: var(--primary-color) !important; color: #fff !important; box-shadow: var(--shadow) !important; } .btn-primary-custom:hover, .btn-primary:hover, .btn-success:hover { background: var(--primary-dark) !important; border-color: var(--primary-dark) !important; color: #fff !important; box-shadow: var(--shadow-md) !important; } .btn-outline-primary-custom, .btn-outline-primary, .btn-outline-success, .btn-outline-secondary, .btn-outline-danger, .btn-outline-dark, .btn-light { background-color: var(--surface) !important; border-color: transparent !important; color: var(--text-primary) !important; box-shadow: var(--shadow) !important; } .btn-outline-primary-custom:hover, .btn-outline-primary:hover { background-color: var(--primary-color) !important; border-color: var(--primary-color) !important; color: #fff !important; box-shadow: var(--shadow-md) !important; } .btn-outline-success:hover { background-color: var(--primary-color) !important; border-color: var(--primary-color) !important; color: #fff !important; box-shadow: var(--shadow-md) !important; } .btn-outline-secondary:hover { background-color: var(--text-secondary) !important; border-color: var(--text-secondary) !important; color: #fff !important; box-shadow: var(--shadow-md) !important; } .btn-outline-danger:hover { background-color: #ef4444 !important; border-color: #ef4444 !important; color: #fff !important; box-shadow: var(--shadow-md) !important; } .btn-outline-dark:hover, .btn-light:hover { background-color: var(--text-secondary) !important; border-color: var(--text-secondary) !important; color: var(--background) !important; box-shadow: var(--shadow-md) !important; } .btn-dark { background-color: var(--text-primary) !important; border-color: var(--text-primary) !important; color: var(--background) !important; box-shadow: var(--shadow) !important; } /* ── Forms: pressed-in surfaces ────────────────────────────────── */ .form-control, .form-select { background-color: var(--surface) !important; border: 1px solid transparent !important; color: var(--text-primary) !important; border-radius: 14px !important; box-shadow: var(--shadow-inset) !important; transition: box-shadow 0.3s ease, background-color 0.25s ease !important; } .form-control:focus, .form-select:focus { background-color: var(--surface) !important; border-color: transparent !important; color: var(--text-primary) !important; box-shadow: var(--shadow-inset), 0 0 0 4px rgba(var(--primary-rgb), 0.15) !important; } .form-control::placeholder { color: var(--text-muted) !important; } .input-group-text { background-color: var(--surface-alt) !important; border: 1px solid transparent !important; color: var(--text-secondary) !important; box-shadow: var(--shadow-inset-sm) !important; } /* ── Navbar: floating neumorphic pill (tablet & desktop) ──────── */ .navbar { background-color: rgba(var(--background-rgb), 0.9) !important; border-bottom: 1px solid transparent !important; backdrop-filter: blur(16px) !important; -webkit-backdrop-filter: blur(16px) !important; box-shadow: var(--shadow-md) !important; } [data-theme="dark"] .navbar { box-shadow: var(--shadow-md) !important; } @media (min-width: 768px) { .navbar { top: 14px !important; left: 18px !important; right: 18px !important; width: auto !important; border-radius: 20px !important; padding: 0.6rem 0 !important; box-shadow: var(--shadow-lg) !important; } [data-theme="dark"] .navbar { box-shadow: var(--shadow-lg) !important; } .navbar.scrolled { background-color: rgba(var(--background-rgb), 0.95) !important; border-bottom-color: transparent !important; padding: 0.5rem 0 !important; box-shadow: var(--shadow-lg) !important; } [data-theme="dark"] .navbar.scrolled { background-color: rgba(28, 32, 40, 0.95) !important; box-shadow: var(--shadow-lg) !important; } body { padding-top: 96px !important; } .reading-progress-container { top: 88px !important; } } /* ── Tablet refinements (768px - 991.98px) ────────────────────── */ @media (min-width: 768px) and (max-width: 991.98px) { /* Hamburger dropdown panel blends with the floating pill */ .navbar-collapse { border-radius: 20px !important; border-color: transparent !important; } body { padding-bottom: 0 !important; } .whatsapp-btn-float { bottom: 24px !important; } /* Hero: tighten the inline 80px top padding that assumed a full-height bar, so tablets don't get excessive empty space */ .hero-section-bg { padding-top: 40px !important; } } /* ── Neumorphic brand icon mark ────────────────────────────────── */ .brand-mark { width: 40px; height: 40px; flex-shrink: 0; border-radius: 13px; background-color: var(--surface) !important; color: var(--primary-color) !important; display: inline-flex; align-items: center; justify-content: center; box-shadow: var(--shadow) !important; transition: box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1), transform 0.3s cubic-bezier(0.16, 1, 0.3, 1); } .brand-mark svg { width: 21px; height: 21px; } a:hover .brand-mark { box-shadow: var(--shadow-inset-sm) !important; transform: translateY(1px); } .mobile-logo .brand-mark { width: 34px; height: 34px; border-radius: 11px; } .mobile-logo .brand-mark svg { width: 17px; height: 17px; } /* ── Uploaded brand logo image ─────────────────────────────────── */ .brand-img { height: 38px; width: auto; max-width: 160px; object-fit: contain; flex-shrink: 0; } .brand-img-sm { height: 28px; max-width: 120px; } .brand-img-footer { height: 44px; max-width: 180px; } /* ── Mobile bars: floating neumorphic pills (phones) ──────────── */ @media (max-width: 767.98px) { .mobile-top-bar { top: 8px !important; left: 12px !important; right: 12px !important; height: 52px !important; border-radius: 16px !important; border-bottom: none !important; background-color: rgba(var(--surface-rgb), 0.92) !important; box-shadow: var(--shadow-md) !important; } .mobile-bottom-bar { left: 12px !important; right: 12px !important; bottom: 12px !important; height: 58px !important; border-radius: 18px !important; border-top: none !important; background-color: rgba(var(--surface-rgb), 0.92) !important; box-shadow: var(--shadow-md) !important; } body { padding-top: 74px !important; padding-bottom: 86px !important; } .whatsapp-btn-float { bottom: 88px !important; } .reading-progress-container { top: 70px !important; } } /* ── Nav pill widgets: pressed-in ──────────────────────────────── */ .theme-toggle, .mobile-theme-toggle, .nav-clock { background-color: var(--surface) !important; border-color: transparent !important; box-shadow: var(--shadow-inset-sm) !important; } /* ── Icon chips, markers & circles: extruded ───────────────────── */ .icon-wrap, .icon-wrapper, .expertise-card-icon, .timeline-marker, .mobile-more-icon-wrapper, .social-circle, .value-icon, .skill-icon-box { box-shadow: var(--shadow) !important; border-color: transparent !important; } /* ── Panels: extruded ──────────────────────────────────────────── */ .accordion-item { border-color: transparent !important; box-shadow: var(--shadow) !important; } .timeline-content { background: var(--surface) !important; border-radius: 12px !important; padding: 1rem 1.25rem !important; box-shadow: var(--shadow) !important; } .feature-item { background: var(--surface) !important; border-color: transparent !important; box-shadow: var(--shadow) !important; } .feature-item:hover { transform: translateY(-2px) !important; box-shadow: var(--shadow-md) !important; } .progress { background-color: var(--surface-alt) !important; box-shadow: var(--shadow-inset-sm) !important; } /* Card hover: consistent soft neu shadow */ .card-hover:hover, .project-card:hover, .blog-card:hover, .post-card:hover, .news-card:hover, .testimonial-card:hover, .hover-lift:hover, .stats-item:hover { box-shadow: var(--shadow-lg) !important; } </style> <script> document.addEventListener('DOMContentLoaded', function() { const readerCard = document.getElementById('reader-card'); if (!readerCard) return; // Settings Key constants const THEME_KEY = 'reader_mode_theme'; const FONT_KEY = 'reader_mode_font'; const SIZE_KEY = 'reader_mode_size'; // Available Options const themes = ['light', 'sepia', 'dark', 'oled']; const fonts = ['serif', 'sans']; const sizes = ['xs', 'sm', 'md', 'lg', 'xl']; // Default Configs const siteTheme = document.documentElement.getAttribute('data-theme') || 'light'; const defaultTheme = siteTheme === 'dark' ? 'dark' : 'light'; let currentTheme = localStorage.getItem(THEME_KEY) || defaultTheme; let currentFont = localStorage.getItem(FONT_KEY) || 'serif'; let currentSize = localStorage.getItem(SIZE_KEY) || 'md'; // Apply initial config applySettings(); // Register Action Listeners (Toolbar) document.getElementById('btn-font-serif').addEventListener('click', () => setFont('serif')); document.getElementById('btn-font-sans').addEventListener('click', () => setFont('sans')); document.getElementById('btn-size-decrease').addEventListener('click', () => adjustSize(-1)); document.getElementById('btn-size-increase').addEventListener('click', () => adjustSize(1)); document.getElementById('theme-light').addEventListener('click', () => setTheme('light')); document.getElementById('theme-sepia').addEventListener('click', () => setTheme('sepia')); document.getElementById('theme-dark').addEventListener('click', () => setTheme('dark')); document.getElementById('theme-oled').addEventListener('click', () => setTheme('oled')); // Register Action Listeners (Sidebar widgets, if present) const sidebarSerif = document.getElementById('sidebar-font-serif'); const sidebarSans = document.getElementById('sidebar-font-sans'); const sidebarSizeDec = document.getElementById('sidebar-size-dec'); const sidebarSizeInc = document.getElementById('sidebar-size-inc'); if (sidebarSerif) sidebarSerif.addEventListener('click', () => setFont('serif')); if (sidebarSans) sidebarSans.addEventListener('click', () => setFont('sans')); if (sidebarSizeDec) sidebarSizeDec.addEventListener('click', () => adjustSize(-1)); if (sidebarSizeInc) sidebarSizeInc.addEventListener('click', () => adjustSize(1)); // Action Core Functions function setTheme(theme) { currentTheme = theme; localStorage.setItem(THEME_KEY, theme); applySettings(); } function setFont(font) { currentFont = font; localStorage.setItem(FONT_KEY, font); applySettings(); } function adjustSize(delta) { let index = sizes.indexOf(currentSize); index = Math.max(0, Math.min(sizes.length - 1, index + delta)); currentSize = sizes[index]; localStorage.setItem(SIZE_KEY, currentSize); applySettings(); } function applySettings() { // 1. Reset theme classes and apply selected themes.forEach(t => readerCard.classList.remove('reader-theme-' + t)); readerCard.classList.add('reader-theme-' + currentTheme); // Update Theme circles highlight themes.forEach(t => { const circle = document.getElementById('theme-' + t); if (circle) { if (t === currentTheme) { circle.classList.add('active'); } else { circle.classList.remove('active'); } } }); // 2. Reset Font classes and apply selected fonts.forEach(f => readerCard.classList.remove('reader-font-' + f)); readerCard.classList.add('reader-font-' + currentFont); // Highlight Font buttons const serifBtns = [document.getElementById('btn-font-serif'), document.getElementById('sidebar-font-serif')]; const sansBtns = [document.getElementById('btn-font-sans'), document.getElementById('sidebar-font-sans')]; serifBtns.forEach(btn => { if (btn) { if (currentFont === 'serif') { btn.classList.add('btn-primary-custom'); btn.classList.remove('btn-light'); } else { btn.classList.add('btn-light'); btn.classList.remove('btn-primary-custom'); } } }); sansBtns.forEach(btn => { if (btn) { if (currentFont === 'sans') { btn.classList.add('btn-primary-custom'); btn.classList.remove('btn-light'); } else { btn.classList.add('btn-light'); btn.classList.remove('btn-primary-custom'); } } }); // 3. Reset Sizing classes and apply selected sizes.forEach(s => readerCard.classList.remove('reader-size-' + s)); readerCard.classList.add('reader-size-' + currentSize); } // Reading Scroll progress calculation const progressBar = document.getElementById('reading-progress-bar'); window.addEventListener('scroll', () => { const documentHeight = document.documentElement.scrollHeight - window.innerHeight; if (documentHeight > 0) { const scrollProgress = (window.pageYOffset / documentHeight) * 100; progressBar.style.width = scrollProgress + '%'; } }); }); </script> <script> // ══════════════════════════════════════════════════════════════ // PWA: Service Worker registration + install prompt // ══════════════════════════════════════════════════════════════ (function () { const PWA_VERSION = '20260805-1'; // ---- Service worker ------------------------------------ if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js?v=' + PWA_VERSION).then((reg) => { // Apply updates (new deploy) on the next load without disruption reg.addEventListener('updatefound', () => { const newWorker = reg.installing; if (!newWorker) return; newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { newWorker.postMessage({ type: 'SKIP_WAITING' }); } }); }); }).catch(() => {}); }); } // ---- Install prompt -------------------------------------- let deferredPrompt = null; const bannerId = 'pwa-install-banner'; const DISMISS_KEY = 'nesh-install-dismissed'; function showInstallBanner() { if (localStorage.getItem(DISMISS_KEY) || document.getElementById(bannerId)) return; const banner = document.createElement('div'); banner.id = bannerId; banner.setAttribute('role', 'dialog'); banner.setAttribute('aria-label', 'Install app'); banner.innerHTML = '<div class="pwa-install-content">' + '<span class="pwa-install-icon">' + '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>' + '</span>' + '<div class="pwa-install-text">' + '<strong>Install NeshDevTech</strong>' + '<span>Get the app on your device</span>' + '</div>' + '<button type="button" class="pwa-install-btn" id="pwa-install-btn">Install</button>' + '<button type="button" class="pwa-install-close" id="pwa-install-close" aria-label="Dismiss">×</button>' + '</div>'; document.body.appendChild(banner); document.getElementById('pwa-install-btn').addEventListener('click', () => { if (!deferredPrompt) return; deferredPrompt.prompt(); deferredPrompt.userChoice.finally(() => { deferredPrompt = null; hideInstallBanner(); }); }); document.getElementById('pwa-install-close').addEventListener('click', () => { localStorage.setItem(DISMISS_KEY, '1'); hideInstallBanner(); }); requestAnimationFrame(() => banner.classList.add('show')); } function hideInstallBanner() { const banner = document.getElementById(bannerId); if (banner) { banner.classList.remove('show'); setTimeout(() => banner.remove(), 250); } } window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferredPrompt = e; showInstallBanner(); }); window.addEventListener('appinstalled', () => { deferredPrompt = null; hideInstallBanner(); localStorage.setItem(DISMISS_KEY, '1'); }); })(); </script> <style> .pwa-install-content { display: flex; align-items: center; gap: 12px; padding: 14px 18px; } #pwa-install-banner { position: fixed; left: 50%; transform: translate(-50%, 20px); bottom: 84px; z-index: 1070; background: var(--surface); color: var(--text-primary); border: 1px solid var(--border); border-radius: 18px; box-shadow: var(--shadow-lg); opacity: 0; transition: opacity .25s cubic-bezier(.16, 1, .3, 1), transform .25s cubic-bezier(.16, 1, .3, 1); width: min(420px, calc(100vw - 32px)); } #pwa-install-banner.show { opacity: 1; transform: translate(-50%, 0); } .pwa-install-icon { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; flex: 0 0 40px; border-radius: 12px; background: rgba(var(--primary-rgb), 0.1); border: 1px solid rgba(var(--primary-rgb), 0.2); color: var(--primary-color); } .pwa-install-icon svg { width: 20px; height: 20px; } .pwa-install-text { display: flex; flex-direction: column; flex: 1; min-width: 0; } .pwa-install-text strong { font-size: 0.92rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .pwa-install-text span { font-size: 0.78rem; color: var(--text-secondary); } .pwa-install-btn { background: var(--primary-color); border: none; color: #fff; font-weight: 600; font-size: 0.85rem; padding: 8px 18px; border-radius: 9999px; cursor: pointer; white-space: nowrap; box-shadow: 0 4px 12px rgba(var(--primary-rgb), 0.25); transition: all .2s ease; } .pwa-install-btn:hover { background: var(--primary-dark); transform: translateY(-1px); } .pwa-install-close { background: transparent; border: none; color: var(--text-muted); font-size: 1.4rem; line-height: 1; cursor: pointer; padding: 2px 6px; border-radius: 50%; transition: background-color .2s ease, color .2s ease; } .pwa-install-close:hover { background: var(--surface-alt); color: var(--text-primary); } @media (min-width: 992px) { #pwa-install-banner { bottom: 24px; } } </style> </div><!-- /.page-wrapper --> </body> </html>