diff --git a/.github/workflows/doc-check-linter.yml b/.github/workflows/doc-check-linter.yml index 32f4766..3c4be3d 100644 --- a/.github/workflows/doc-check-linter.yml +++ b/.github/workflows/doc-check-linter.yml @@ -61,7 +61,7 @@ jobs: echo "⚠️ Skipping GPG import: No secrets available (forked PR or missing keys)." exit 0 fi - + # Function to import and trust a GPG key import_key() { local key="$1" @@ -76,35 +76,35 @@ jobs: fi fi } - + # Import your own keys import_key "$GPG_PUBLIC_KEY" "GPG_PUBLIC_KEY" import_key "$GPG_PUBLIC_KEY_1" "GPG_PUBLIC_KEY_1" - + # ✅ Import GitHub’s official signing keys (commit + merge) echo "🌐 Importing GitHub official GPG signing keys..." curl -fsSL https://github.com/web-flow.gpg | gpg --import || true curl -fsSL https://github.com/actions/runner-images/blob/main/images/github-bot.gpg?raw=true | gpg --import || true echo "✅ Imported GitHub web-flow and merge bot keys successfully." - + echo "🎉 All available GPG public keys imported successfully." - + # ---------- Verify latest commit signature ---------- - name: Verify latest commit signature if: ${{ github.event.pull_request.head.repo.full_name == github.repository || github.event_name != 'pull_request' }} run: | LATEST_COMMIT=$(git rev-parse HEAD) echo "🔍 Checking latest commit: $LATEST_COMMIT" - + SIG=$(git log --show-signature -1 "$LATEST_COMMIT") echo "$SIG" - + # ✅ Include all trusted fingerprints - TRUSTED_KEYS="7F4C7CA953E1C09E D432152833DA3244 88F6CD4E295C9062 BE677DAEFE33CB57 E9A0198A7C7B3BD6 C97540DA6C9FA85C" + TRUSTED_KEYS="7F4C7CA953E1C09E D432152833DA3244 88F6CD4E295C9062 BE677DAEFE33CB57 C97540DA6C9FA85C" GITHUB_COMMIT_KEY="4AEE18F83AFDEB23" # GitHub web-flow GITHUB_MERGE_KEY="B5690EEEBB952194" # GitHub merge bot TRUSTED="$TRUSTED_KEYS $GITHUB_COMMIT_KEY $GITHUB_MERGE_KEY" - + if echo "$SIG" | grep -q "Good signature"; then for key in $TRUSTED; do if echo "$SIG" | grep -q "$key"; then @@ -113,11 +113,11 @@ jobs: fi done fi - + echo "❌ Commit is not GPG signed with a trusted key!" exit 1 - + # ---------- Optional status for skipped forked PRs ---------- - name: Skip GPG checks for external PRs if: ${{ github.event.pull_request.head.repo.full_name != github.repository && github.event_name == 'pull_request' }} - run: echo "🟡 Skipping GPG verification for external PR (no access to secrets)." + run: echo "🟡 Skipping GPG verification for external PR (no access to secrets)." \ No newline at end of file diff --git a/assets/vimal-patel.vcf b/assets/vimal-patel.vcf new file mode 100644 index 0000000..6eeb8e1 --- /dev/null +++ b/assets/vimal-patel.vcf @@ -0,0 +1,14 @@ +BEGIN:VCARD +VERSION:3.0 +N:Patel;Vimal;;; +FN:Vimal Patel +ORG:Vimal Tech +TITLE:Software Developer +EMAIL;TYPE=INTERNET,PREF:vimal.patel@vimaltech.dev +TEL;TYPE=CELL,VOICE:+919638474047 +ADR;TYPE=WORK:;;Valsad;Gujarat;396001;India +URL:https://vimaltech.dev +URL:https://www.linkedin.com/in/vimaltech/ +URL:https://www.instagram.com/vimaltech/ +NOTE:Trusted software partner. +END:VCARD diff --git a/contact.html b/contact.html new file mode 100644 index 0000000..cda230a --- /dev/null +++ b/contact.html @@ -0,0 +1,380 @@ + + + + + + + Vimal Tech • Contact + + + + + + + + + + + + + + + +
+
+ Home |  + Services |  + Expertise |  + Contact +
+
+
+ + Vimal Patel +
+ +
+ +
+ + +
+ +
+ +

Let’s Build Something Great

+

Great software isn’t just written—it’s + crafted with intention, discipline, and a drive to create clear business value. + If you’re ready to ship with confidence and quality, I’m here to help.” +

+
+
+

Quick Recap

+

Tools and technologies I love working with.

+ +
+ React + Node.js + Angular + MongoDB + Flutter + Firebase + TypeScript + Tailwind + Vite + HTML + CSS + JS + Bootstrap +
+
+
+ + +
+
+

Get in touch

+

I'd love to hear from you. You can connect me via email, phone, or + socials. I'll get back within 1–2 business days. +

+ +
+
+ +
Emailvimal.patel@vimaltech.dev
+
+
+ +
Phone+91 9638 474047
+
+
+ + +
+
+ +
+
+ +
+
+ +
+ +
+ + +

+ Let’s Inspire By Technologies +

+ + +

+ “Modern web and mobile apps built with a focus on clean architecture, + predictable delivery, and maintainable code. Even as a newcomer to freelancing, + I bring discipline, curiosity, and a commitment to building long-lasting solutions.” +

+ +
+
+ + + + Reliable delivery +
+
+ + + + + Clean architecture +
+
+ + + + Scaling with you +
+
+
+
+ + +
+
+

Contact Me

+

If you’d like to collaborate, feel free to reach out:

+ +
+
+
+ + + + + +
+ + + + + + \ No newline at end of file diff --git a/css/common.css b/css/common.css index 5db8935..6e020d1 100644 --- a/css/common.css +++ b/css/common.css @@ -168,36 +168,6 @@ footer { letter-spacing: 1px; } -/* =================================================== - ===== Contact Section ===== -=================================================== */ -#contact { - text-align: center; - background-color: #929599; -} - -body.dark #contact { - background-color: #467db4; - color: #000; -} - -.contact-links a { - display: inline-block; - margin: 10px; - padding: 12px 20px; - background: #ff9800; - color: #fff; - border-radius: 30px; - text-decoration: none; - font-weight: 500; - transition: background 0.3s, transform 0.3s; -} - -.contact-links a:hover { - background: #e68900; - transform: translateY(-3px); -} - /* =================================================== ===== Floating Dark Mode Toggle ===== =================================================== */ diff --git a/css/contact.css b/css/contact.css new file mode 100644 index 0000000..3f75e53 --- /dev/null +++ b/css/contact.css @@ -0,0 +1,533 @@ +:root { + --bg: #0b1020; + --panel: #0f1629; + --panel-2: #121a33; + --border: #1f2a4b; + --text: #e8ecf4; + --muted: #a9b3c7; + --brand: #7aa7ff; + --brand-2: #9b8cff; + --ok: #2ad4a1; + --warn: #ffb86b; + --error: #ff6b6b; + --shadow: 0 10px 30px rgba(0, 0, 0, .35); + --radius-xl: 18px; + --radius-lg: 14px; + --radius: 12px; +} + +* { + box-sizing: border-box; +} + +html, +body { + height: 100%; +} + +body { + margin: 0; + font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji"; + color: var(--text); + background: radial-gradient(1200px 1200px at 10% -10%, rgba(122, 167, 255, .18), transparent 50%), + radial-gradient(900px 900px at 100% 10%, rgba(155, 140, 255, .18), transparent 40%), + linear-gradient(180deg, #070b17, #0b1020); + overflow-x: hidden; +} + +.noise { + position: fixed; + inset: 0; + pointer-events: none; + background-image: url('data:image/svg+xml;utf8,'); + opacity: .4; + mix-blend-mode: overlay +} + +.container { + max-width: 1100px; + margin: 60px auto; + padding: 0 20px; +} + +header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 28px; +} + +.brand { + display: flex; + align-items: center; + gap: 12px; + font-weight: 700; + letter-spacing: .4px; + font-size: 18px; +} + +.brand .logo { + width: 34px; + height: 34px; + display: grid; + place-items: center; + border-radius: 10px; + background: linear-gradient(135deg, var(--brand), var(--brand-2)); + box-shadow: var(--shadow); +} + +.brand .logo svg { + width: 20px; + height: 20px; + color: white +} + +.theme-toggle { + border: 1px solid var(--border); + background: linear-gradient(180deg, var(--panel), var(--panel-2)); + color: var(--text); + border-radius: 999px; + padding: 10px 14px; + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + box-shadow: var(--shadow); +} + +.theme-toggle span { + font-size: 12px; + color: var(--muted); +} + +.grid { + display: grid; + grid-template-columns: 1.05fr .95fr; + gap: 26px; +} + +@media (max-width: 920px) { + .grid { + grid-template-columns: 1fr; + } +} + +.card { + border: 1px solid var(--border); + background: linear-gradient(180deg, rgba(255, 255, 255, .02), rgba(255, 255, 255, .01)); + border-radius: var(--radius-xl); + box-shadow: var(--shadow); + overflow: hidden; +} + +.panel { + padding: 26px; + backdrop-filter: blur(8px); +} + +.title { + font-size: 26px; + margin: 0 0 6px; + letter-spacing: .3px +} + +.subtitle { + margin: 0 0 22px; + color: var(--muted); + line-height: 1.6 +} + +.contact-list { + display: grid; + gap: 12px; + margin: 16px 0 0 +} + +.contact-item { + display: flex; + gap: 14px; + align-items: flex-start; + padding: 12px; + border: 1px dashed var(--border); + border-radius: var(--radius-lg); + background: linear-gradient(180deg, rgba(255, 255, 255, .02), rgba(255, 255, 255, .01)); +} + +.contact-item svg { + flex: 0 0 20px; + opacity: .9 +} + +.contact-item strong { + display: block; + font-size: 14px +} + +.contact-item span { + color: var(--muted); + font-size: 13px +} + +.socials { + display: flex; + gap: 10px; + margin-top: 16px +} + +.icon-btn { + border: 1px solid var(--border); + background: linear-gradient(180deg, var(--panel), var(--panel-2)); + border-radius: 12px; + padding: 10px; + display: grid; + place-items: center; + cursor: pointer; + transition: transform .14s ease; +} + +.icon-btn:hover { + transform: translateY(-2px) +} + +.icon-btn svg { + width: 20px; + height: 20px +} + +/* Map placeholder */ +.map { + height: 240px; + border-top: 1px solid var(--border); + background: conic-gradient(from 180deg at 0% 0%, rgba(122, 167, 255, .12), rgba(155, 140, 255, .08), rgba(42, 212, 161, .10)); + position: relative; +} + +.map::after { + content: "Map preview"; + position: absolute; + bottom: 10px; + right: 12px; + font-size: 12px; + color: var(--muted); +} + +/* Light theme */ +.light { + --bg: #f6f8fd; + --panel: #fff; + --panel-2: #f9fafc; + --border: #e6eaf3; + --text: #0d1321; + --muted: #5a6275; + --shadow: 0 10px 30px rgba(19, 33, 68, .08); + background: radial-gradient(1400px 1200px at 10% -10%, rgba(122, 167, 255, .25), transparent 50%), + radial-gradient(900px 900px at 100% 10%, rgba(155, 140, 255, .22), transparent 40%), + linear-gradient(180deg, #eef3ff, #f6f8fd); +} + +.light .label { + background: linear-gradient(180deg, #fff, #fff); +} + +/* Polishing visuals */ +.title em { + font-style: normal; + background: linear-gradient(90deg, var(--brand), var(--brand-2)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.card { + position: relative; + overflow: hidden; +} + +.card::after { + content: ""; + position: absolute; + inset: -1px; + border-radius: inherit; + padding: 1px; + background: linear-gradient(120deg, rgba(122, 167, 255, .6), rgba(42, 212, 161, .4), rgba(155, 140, 255, .6)); + -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + opacity: .25; + transition: opacity .25s ease; + pointer-events: none +} + +.card:hover::after { + opacity: .55 +} + +.fade-in { + animation: fadeInUp .6s ease both +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(6px) + } + + to { + opacity: 1; + transform: none + } +} + +.contact-links { + display: flex; + flex-wrap: wrap; + gap: 12px; +} + +.contact-links a { + border: 1px solid var(--border); + background: linear-gradient(135deg, rgba(255, 255, 255, .06), rgba(255, 255, 255, .02)); + border-radius: 14px; + padding: 12px 16px; + text-decoration: none; + color: var(--text); + display: flex; + align-items: center; + gap: 10px; + font-weight: 600; + box-shadow: var(--shadow); + position: relative; + overflow: hidden; + transition: transform .18s ease, box-shadow .18s ease; +} + +.contact-links a::before { + content: ""; + position: absolute; + inset: 0; + background: radial-gradient(120px 120px at var(--mx, 0) var(--my, 0), rgba(122, 167, 255, .25), transparent 60%); + opacity: 0; + transition: opacity .2s ease; +} + +.contact-links a:hover { + transform: translateY(-2px); +} + +.contact-links a:hover::before { + opacity: 1 +} + +.quote { + font-size: 18px; + line-height: 1.8; + color: var(--text); +} + +.quote strong { + background: linear-gradient(90deg, var(--brand), var(--ok)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +.badge { + display: inline-flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 999px; + border: 1px solid var(--border); + background: linear-gradient(180deg, var(--panel), var(--panel-2)); + font-size: 12px; + color: var(--muted) +} + +.tech-badges { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-top: 12px; +} + +.sparkle { + position: absolute; + right: -40px; + top: -40px; + width: 200px; + height: 200px; + background: radial-gradient(closest-side, rgba(122, 167, 255, .18), transparent 65%); + filter: blur(8px); + pointer-events: none +} + +.icon { + width: 18px; + height: 18px +} + +/* Hover underline for Get in touch social links */ +.contact-item span:hover, +.contact-item strong:hover { + text-decoration: underline; + cursor: pointer; +} + +/* Full-width contact buttons on mobile */ +@media (max-width: 600px) { + .contact-links a { + width: 100%; + justify-content: center; + } +} + +/* Ripple effect for buttons */ +.contact-links a { + position: relative; + overflow: hidden; +} + +.contact-links a .ripple { + position: absolute; + border-radius: 50%; + transform: scale(0); + animation: ripple 0.6s linear; + background: rgba(255, 255, 255, 0.35); + pointer-events: none; +} + +@keyframes ripple { + to { + transform: scale(4); + opacity: 0 + } +} + +@media (max-width: 600px) { + .contact-links a { + width: 100%; + justify-content: center; + } +} + +/* Micro-interactions & delightful polish */ +.card { + transition: transform .25s ease, box-shadow .25s ease +} + +.card:hover { + transform: translateY(-3px) +} + +.brand .logo { + transition: transform .3s ease +} + +.brand:hover .logo { + transform: rotate(-8deg) scale(1.05) +} + +.theme-toggle { + transition: transform .15s ease +} + +.theme-toggle:active { + transform: scale(.96) +} + +.map { + background-size: 200% 200%; + animation: shimmer 6s ease-in-out infinite; +} + +@keyframes shimmer { + 0% { + background-position: 0% 0% + } + + 50% { + background-position: 100% 50% + } + + 100% { + background-position: 0% 0% + } +} + +.contact-item { + transition: transform .2s ease, background .2s ease +} + +.contact-item:hover { + transform: translateX(4px) +} + +.contact-item a { + color: inherit; + text-decoration: none; + border-bottom: 1px dashed transparent; + transition: border-color .2s ease +} + +.contact-item a:hover { + border-color: var(--brand) +} + +.card.in-view { + animation: fadeInUp .6s var(--delay, 0ms) ease both +} + +/* Disable micro-interactions specifically for the Get in touch card */ +section[aria-labelledby="contact-info-title"] .contact-item { + transition: none; + transform: none; +} + +section[aria-labelledby="contact-info-title"] .contact-item a { + border: 0; + text-decoration: none; + color: var(--muted); +} + +section[aria-labelledby="contact-info-title"] .contact-item a:hover { + text-decoration: underline; +} + +/* ===== Scroll to Top Button ===== */ +#scrollTopBtn { + position: fixed; + bottom: 60px; + right: 20px; + width: 46px; + height: 46px; + border-radius: 14px; + border: 1px solid var(--border); + background: linear-gradient(180deg, var(--panel), var(--panel-2)); + box-shadow: var(--shadow); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + opacity: 0; + transform: translateY(12px); + pointer-events: none; + transition: opacity .25s ease, transform .25s ease; + z-index: 999; +} + +#scrollTopBtn svg { + width: 20px; + height: 20px; + color: var(--text); +} + +#scrollTopBtn.show { + opacity: 1; + transform: translateY(0); + pointer-events: auto; +} + +#scrollTopBtn:active { + transform: scale(.94); +} \ No newline at end of file diff --git a/expertise.html b/expertise.html index 88006af..72b5a5e 100644 --- a/expertise.html +++ b/expertise.html @@ -4,7 +4,7 @@ - Vimal Tech - Expertise + Vimal Tech • Expertise @@ -69,7 +69,7 @@
  • Services
  • -
  • Contact
  • +
  • Contact
  • @@ -244,20 +244,6 @@

    Portfolio

    - -
    -

    Contact Me

    -

    If you’d like to collaborate, feel free to reach out:

    - -
    - diff --git a/index.html b/index.html index 4d750e9..3c7faec 100644 --- a/index.html +++ b/index.html @@ -67,7 +67,7 @@
  • Expertise
  • Services
  • -
  • Contact
  • +
  • Contact
  • @@ -160,24 +160,159 @@

    Live Websites

    Visitors

    - 👋 Total Visitors: Loading... + 👋 Total Counts:
    Loading...

    - -
    -

    Contact Me

    -

    If you’d like to collaborate, feel free to reach out:

    -

    + +
    +

    Projects

    +
    +
    +

    Front-End Projects

    +
    + + +
    +
    +
    +
    +

    Back-End Projects

    +
    +
    +

    PTC Windchill (Java Product)

    +

    Customised Java Client for PTC Windchill PDMLink as Shadow Resource.

    +
    +
    +

    Parking System (POC)

    +

    Rest API, Microservices, Docker + Swagger-UI, Postman.

    +
    +
    +

    Data Streaming App (POC)

    +

    Spring Boot + Kafka + Spark + Docker for Real-Time Data Processing.

    +
    +
    +

    Health Care Center Management

    +

    Core Java, Spring Framework and Hibernate + JSP + Oracle

    +
    +
    +

    Consultant Return On Investment

    +

    Core Java, JDBC, DAO Design Pattern, Struts 2.0 Framework. + Oracle

    +
    +
    +

    E-commerce Backend Best Practices

    +

    REST API backend with Java, Spring Boot, and Hibernate + Maven, MySQL

    +
    +
    +

    Chat Application

    +

    Real-Time Chat with Java WebSocket & Spring Boot.

    +
    +
    +

    Desktop Chat Application (Local Area Network)

    +

    Java Remote Method Invocation + Stubs + Skeletons, Swing Framework.

    +
    +
    +

    + + +
    +
    +

    Portfolio

    + View Overview +
    +
    + diff --git a/js/contact.js b/js/contact.js new file mode 100644 index 0000000..721c308 --- /dev/null +++ b/js/contact.js @@ -0,0 +1,102 @@ + +// Year +document.getElementById('year').textContent = new Date().getFullYear(); + +// Theme toggle with localStorage persistence +const root = document.documentElement; +const themeToggle = document.getElementById('themeToggle'); +const sun = document.getElementById('sun'); +const moon = document.getElementById('moon'); + +const setTheme = (mode) => { + if (mode === 'light') { + root.classList.add('light'); + sun.style.display = 'none'; + moon.style.display = ''; + themeToggle.setAttribute('aria-pressed', 'true'); + } else { + root.classList.remove('light'); + sun.style.display = ''; + moon.style.display = 'none'; + themeToggle.setAttribute('aria-pressed', 'false'); + } + localStorage.setItem('prefers-theme', mode); +} + +const saved = localStorage.getItem('prefers-theme'); +if (saved) { setTheme(saved) } +themeToggle.addEventListener('click', () => { + const isLight = root.classList.contains('light'); + setTheme(isLight ? 'dark' : 'light'); +}); + +// Micro-interactions: gentle tilt on hover for cards +document.querySelectorAll('.card').forEach((card, i) => { + if (card.dataset.static === 'true') { return; } + card.style.setProperty('--delay', (i * 80) + 'ms'); + let rAF; + const onMove = (e) => { + const rect = card.getBoundingClientRect(); + const x = (e.clientX - rect.left) / rect.width - .5; + const y = (e.clientY - rect.top) / rect.height - .5; + cancelAnimationFrame(rAF); + rAF = requestAnimationFrame(() => { + card.style.transform = `rotateX(${-y * 3}deg) rotateY(${x * 3}deg) translateY(-3px)`; + }); + }; + const reset = () => { card.style.transform = '' }; + card.addEventListener('mousemove', onMove); + card.addEventListener('mouseleave', reset); +}); + +// Reveal on scroll +const io = new IntersectionObserver((entries) => { + entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('in-view'); io.unobserve(entry.target); } }); +}, { threshold: .2 }); +document.querySelectorAll('.card').forEach(el => io.observe(el)); + +/* ============================ + Scroll-to-Top Button +============================ */ +document.addEventListener("DOMContentLoaded", () => { + const scrollTopBtn = document.getElementById("scrollTopBtn"); + + window.addEventListener("scroll", () => { + if (window.scrollY > 180) { + scrollTopBtn.classList.add("show"); + } else { + scrollTopBtn.classList.remove("show"); + } + }); + + scrollTopBtn.addEventListener("click", () => { + window.scrollTo({ top: 0, behavior: "smooth" }); + }); + + function makeNoise() { + const el = document.querySelector('.noise'); + if (!el) return; + + const size = 80; // texture tile size + const c = document.createElement('canvas'); + c.width = c.height = size; + + const ctx = c.getContext('2d', { willReadFrequently: false }); + const img = ctx.createImageData(size, size); + const data = img.data; + + // grayscale random pixels with low alpha + for (let i = 0; i < data.length; i += 4) { + const v = Math.random() * 255; + data[i] = data[i + 1] = data[i + 2] = v; // R,G,B + data[i + 3] = 12; // alpha (0–255) -> ~0.05 opacity per pixel + } + ctx.putImageData(img, 0, 0); + + el.style.backgroundImage = `url(${c.toDataURL('image/png')})`; + el.style.backgroundRepeat = 'repeat'; + }; + +}); + + diff --git a/js/services.js b/js/services.js index 80cdf9a..910dad7 100644 --- a/js/services.js +++ b/js/services.js @@ -124,4 +124,3 @@ function updateToggleIcon() { darkModeToggle.style.color = "#fff"; } } - diff --git a/services.html b/services.html index 4750bb6..95038f3 100644 --- a/services.html +++ b/services.html @@ -4,7 +4,7 @@ - Vimal Tech - Services + Vimal Tech • Services @@ -64,7 +64,7 @@
  • Live Websites
  • Expertise
  • -
  • Contact
  • +
  • Contact
  • @@ -146,20 +146,6 @@

    Live Websites

    - -
    -

    Contact Me

    -

    If you’d like to collaborate, feel free to reach out:

    - -
    -