|
132 | 132 | flex-shrink: 0; |
133 | 133 | } |
134 | 134 | .brand-mark svg { width: 18px; height: 18px; } |
| 135 | +.brand-logo-link { display: flex; align-items: center; flex-shrink: 0; text-decoration: none; } |
| 136 | +.brand-logo-img { height: 38px; width: auto; max-width: 200px; object-fit: contain; display: block; } |
135 | 137 | .brand-name { font-weight: 700; font-size: 1.05rem; letter-spacing: 0.3px; } |
136 | 138 | .brand-name { text-decoration: none; color: var(--white); } |
137 | 139 | .brand-sub { font-family: var(--font-mono); font-size: 0.62rem; color: var(--g3); letter-spacing: 1.5px; text-transform: uppercase; text-decoration: none; transition: color 0.18s ease; } |
|
328 | 330 | Trusted in production section |
329 | 331 | ============================================================ */ |
330 | 332 | .trusted { border-top: 1px solid var(--g1); border-bottom: 1px solid var(--g1); background: var(--bg1); padding: 52px 0; } |
331 | | -.trusted-inner { display: flex; flex-direction: column; align-items: center; gap: 32px; text-align: center; } |
| 333 | +.trusted-inner { display: flex; flex-direction: column; align-items: center; gap: 28px; text-align: center; } |
332 | 334 | .trusted-label { |
333 | 335 | font-family: var(--font-mono); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 2.5px; |
334 | 336 | color: var(--g3); font-weight: 600; |
|
338 | 340 | content: ""; flex: 1; height: 1px; width: 48px; background: linear-gradient(90deg, transparent, var(--g1)); |
339 | 341 | } |
340 | 342 | .trusted-label::after { background: linear-gradient(90deg, var(--g1), transparent); } |
341 | | -.trusted-cards { display: flex; gap: 24px; flex-wrap: wrap; justify-content: center; } |
342 | | -.trusted-card { |
| 343 | + |
| 344 | +/* Single wide spotlight banner, not a grid/list item */ |
| 345 | +.trusted-spotlight { |
| 346 | + display: flex; align-items: center; gap: 32px; |
| 347 | + max-width: 680px; width: 100%; |
343 | 348 | background: var(--bg2); border: 1px solid var(--g1); border-radius: var(--radius-lg); |
344 | | - padding: 28px 36px 24px; display: flex; flex-direction: column; align-items: center; gap: 16px; |
345 | | - text-decoration: none; max-width: 340px; width: 100%; |
346 | | - transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s cubic-bezier(0.2,0.8,0.2,1); |
| 349 | + padding: 30px 42px; |
| 350 | + text-decoration: none; |
347 | 351 | position: relative; overflow: hidden; |
| 352 | + transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s cubic-bezier(0.2,0.8,0.2,1); |
348 | 353 | } |
349 | | -.trusted-card::before { |
| 354 | +.trusted-spotlight::before { |
350 | 355 | content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px; |
351 | 356 | background: linear-gradient(90deg, transparent, rgba(249,115,22,0.35), transparent); |
352 | 357 | opacity: 0; transition: opacity 0.25s ease; |
353 | 358 | } |
354 | | -.trusted-card:hover { border-color: var(--g2); box-shadow: 0 8px 32px -12px rgba(0,0,0,0.7); transform: translateY(-3px); } |
355 | | -.trusted-card:hover::before { opacity: 1; } |
356 | | -.trusted-logo { |
357 | | - height: 36px; width: auto; max-width: 160px; |
358 | | - filter: brightness(0) invert(1) opacity(0.7); |
| 359 | +.trusted-spotlight:hover { border-color: var(--g2); box-shadow: 0 8px 32px -12px rgba(0,0,0,0.7); transform: translateY(-3px); } |
| 360 | +.trusted-spotlight:hover::before { opacity: 1; } |
| 361 | +.trusted-spotlight-logo { |
| 362 | + height: 46px; width: auto; max-width: 170px; flex-shrink: 0; |
| 363 | + filter: brightness(0) invert(1) opacity(0.75); |
359 | 364 | transition: opacity 0.2s ease; |
360 | 365 | } |
361 | | -.trusted-card:hover .trusted-logo { filter: brightness(0) invert(1) opacity(1); } |
362 | | -.trusted-card-body { display: flex; flex-direction: column; align-items: center; gap: 10px; } |
363 | | -.trusted-desc { font-size: 0.82rem; color: var(--g3); line-height: 1.55; max-width: 30ch; text-align: center; } |
364 | | -.trusted-desc strong { color: var(--g4); font-weight: 600; } |
| 366 | +.trusted-spotlight:hover .trusted-spotlight-logo { filter: brightness(0) invert(1) opacity(1); } |
| 367 | +.trusted-spotlight-divider { width: 1px; align-self: stretch; background: var(--g1); flex-shrink: 0; } |
| 368 | +.trusted-spotlight-body { display: flex; flex-direction: column; align-items: flex-start; gap: 10px; text-align: left; } |
| 369 | +.trusted-desc { font-size: 0.88rem; color: var(--g4); line-height: 1.55; } |
| 370 | +.trusted-desc strong { color: var(--g5); font-weight: 600; } |
365 | 371 | .trusted-badge { font-family: var(--font-mono); font-size: 0.62rem; padding: 3px 9px; border-radius: 4px; letter-spacing: 0.5px; } |
366 | 372 | .trusted-badge.green { color: var(--low3); background: var(--lowb); border: 1px solid rgba(34,197,94,0.25); } |
367 | 373 |
|
|
409 | 415 | .stage-lang.rs { color: var(--high3); background: var(--highb); border: 1px solid rgba(249,115,22,0.25); } |
410 | 416 |
|
411 | 417 | /* Feature grid */ |
412 | | -.features-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; } |
| 418 | +.features-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 18px; } |
413 | 419 | .feature-card { |
| 420 | + grid-column: span 2; |
414 | 421 | background: var(--bg1); border: 1px solid var(--g1); border-radius: var(--radius-lg); |
415 | 422 | padding: 26px; display: flex; flex-direction: column; gap: 12px; |
416 | 423 | transition: transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1), border-color 0.25s ease, box-shadow 0.25s ease; |
417 | 424 | position: relative; overflow: hidden; |
418 | 425 | } |
| 426 | +/* Last row has only 2 cards (8 total in a 3-per-row layout) — center them as a pair */ |
| 427 | +.features-grid .feature-card:nth-child(7) { grid-column: 2 / span 2; } |
| 428 | +.features-grid .feature-card:nth-child(8) { grid-column: 4 / span 2; } |
419 | 429 | .feature-card::before { |
420 | 430 | content: ""; position: absolute; top: 0; left: 0; right: 0; height: 2px; |
421 | 431 | background: var(--grad-h); opacity: 0; transition: opacity 0.25s ease; |
|
681 | 691 | .stage:nth-child(4) { border-top: none; border-radius: 0 0 var(--radius-lg) 0; } |
682 | 692 | .stage:nth-child(2)::after { display: none; } |
683 | 693 | .features-grid { grid-template-columns: 1fr 1fr; } |
| 694 | + .features-grid .feature-card { grid-column: auto; } |
684 | 695 | .bench-grid { grid-template-columns: 1fr; } |
685 | 696 | .docs-layout { grid-template-columns: 1fr; gap: 0; } |
686 | 697 | .docs-rail { display: none; } |
|
712 | 723 | .bar-row { grid-template-columns: 70px 1fr 84px; } |
713 | 724 | .hero-proof { grid-template-columns: repeat(3, minmax(0, 1fr)); } |
714 | 725 | .trusted-inner { gap: 20px; } |
715 | | - .trusted-card { padding: 22px 24px 20px; } |
| 726 | + .trusted-spotlight { flex-direction: column; text-align: center; padding: 26px 24px; gap: 18px; } |
| 727 | + .trusted-spotlight-divider { width: 100%; height: 1px; } |
| 728 | + .trusted-spotlight-body { align-items: center; text-align: center; } |
716 | 729 | } |
717 | 730 | @media (min-width: 761px) { .mobile-menu { display: none !important; } } |
718 | 731 |
|
|
763 | 776 | <nav class="topnav"> |
764 | 777 | <div class="topnav-inner"> |
765 | 778 | <div class="brand"> |
766 | | - <a class="brand-mark" href="#top" aria-label="PySpector home"> |
767 | | - <svg viewBox="0 0 24 24" fill="none" stroke="#e8f0f5" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M7 18 V6 h5.5 a4 4 0 0 1 0 8 H9"/></svg> |
| 779 | + <a class="brand-logo-link" href="#top" aria-label="PySpector home"> |
| 780 | + <img class="brand-logo-img" src="logo.png" alt="PySpector" /> |
768 | 781 | </a> |
769 | | - <span> |
770 | | - <a class="brand-name" href="#top">PySpector</a><br/> |
771 | | - <a class="brand-sub" href="https://www.securitycert.it/" target="_blank" rel="noopener">Powered by SecurityCert</a> |
772 | | - </span> |
| 782 | + <a class="brand-sub" href="https://www.securitycert.it/" target="_blank" rel="noopener">Powered by SecurityCert</a> |
773 | 783 | </div> |
774 | 784 |
|
775 | 785 | <ul class="nav-links" id="main-nav"> |
@@ -831,10 +841,10 @@ <h1>Trace taint across your entire codebase. <span class="grad-text">Before atta |
831 | 841 | <div class="hero-proof"> |
832 | 842 | <div class="proof-item"><span class="proof-num"><em class="count" data-count="71" data-suffix="%">71%</em> faster</span><span class="proof-label">than Bandit</span></div> |
833 | 843 | <div class="proof-item"><span class="proof-num"><em class="count" data-count="16.6" data-decimals="1" data-suffix="×">16.6×</em> faster</span><span class="proof-label">than Semgrep</span></div> |
834 | | - <div class="proof-item"><span class="proof-num"><em class="count" data-count="25000" data-suffix="+">25,000+</em></span><span class="proof-label">lines / second</span></div> |
| 844 | + <div class="proof-item"><a href="https://github.com/ParzivalHack/PySpector" target="_blank" rel="noopener" class="proof-link"><span class="proof-num"><em id="star-count">145+</em></span><span class="proof-label">GitHub stars</span></a></div> |
835 | 845 | <div class="proof-item"><span class="proof-num"><em class="count" data-count="3">3</em></span><span class="proof-label">analysis engines</span></div> |
836 | | - <div class="proof-item"><a href="https://pepy.tech/projects/pyspector" target="_blank" rel="noopener" class="proof-link"><span class="proof-num"><em class="count" data-count="15000" data-suffix="+">15,000+</em></span><span class="proof-label">downloads</span></a></div> |
837 | | - <div class="proof-item"><a href="https://github.com/ParzivalHack/PySpector/graphs/contributors?all=1" target="_blank" rel="noopener" class="proof-link"><span class="proof-num"><em class="count" data-count="15" data-suffix="+">15+</em></span><span class="proof-label">unique contributors</span></a></div> |
| 846 | + <div class="proof-item"><a href="https://pepy.tech/projects/pyspector" target="_blank" rel="noopener" class="proof-link"><span class="proof-num"><em class="count" data-count="30000" data-suffix="+">30,000+</em></span><span class="proof-label">downloads</span></a></div> |
| 847 | + <div class="proof-item"><a href="https://github.com/ParzivalHack/PySpector/graphs/contributors?all=1" target="_blank" rel="noopener" class="proof-link"><span class="proof-num"><em class="count" data-count="20" data-suffix="">20</em></span><span class="proof-label">unique contributors</span></a></div> |
838 | 848 | </div> |
839 | 849 | </div> |
840 | 850 |
|
@@ -883,15 +893,14 @@ <h1>Trace taint across your entire codebase. <span class="grad-text">Before atta |
883 | 893 | <div class="trusted"> |
884 | 894 | <div class="container trusted-inner"> |
885 | 895 | <span class="trusted-label">Trusted by</span> |
886 | | - <div class="trusted-cards"> |
887 | | - <a class="trusted-card" href="https://satori.ci/" target="_blank" rel="noopener" aria-label="SatoriCI — core scan engine"> |
888 | | - <img class="trusted-logo" src="https://satori.ci/img/logo.svg" alt="SatoriCI" loading="lazy" /> |
889 | | - <div class="trusted-card-body"> |
890 | | - <p class="trusted-desc"><strong>SatoriCI</strong> integrated PySpector as a <strong>core SAST scanning engine</strong>, using it actively to scan <strong>30,000+ repositories</strong> at scale across the Satori platform.</p> |
891 | | - <span class="trusted-badge green">Production-Proven✔</span> |
892 | | - </div> |
893 | | - </a> |
894 | | - </div> |
| 896 | + <a class="trusted-spotlight" href="https://satori.ci/" target="_blank" rel="noopener" aria-label="SatoriCI — core scan engine"> |
| 897 | + <img class="trusted-spotlight-logo" src="https://satori.ci/img/logo.svg" alt="SatoriCI" loading="lazy" /> |
| 898 | + <div class="trusted-spotlight-divider"></div> |
| 899 | + <div class="trusted-spotlight-body"> |
| 900 | + <p class="trusted-desc"><strong>SatoriCI</strong> integrated PySpector as a <strong>core SAST scanning engine</strong>, using it actively to scan <strong>30,000+ repositories</strong> at scale across the Satori platform.</p> |
| 901 | + <span class="trusted-badge green">Production-Proven✔</span> |
| 902 | + </div> |
| 903 | + </a> |
895 | 904 | </div> |
896 | 905 | </div> |
897 | 906 |
|
@@ -1750,8 +1759,10 @@ <h2>Ship Python with confidence.</h2> |
1750 | 1759 | <div class="footer-grid"> |
1751 | 1760 | <div class="footer-brand"> |
1752 | 1761 | <div class="brand"> |
1753 | | - <a class="brand-mark" href="#top" aria-label="Back to top"><svg viewBox="0 0 24 24" fill="none" stroke="#e8f0f5" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M7 18 V6 h5.5 a4 4 0 0 1 0 8 H9"/></svg></a> |
1754 | | - <span><a class="brand-name" href="#top">PySpector</a><br/><a class="brand-sub" href="https://www.securitycert.it/" target="_blank" rel="noopener">Powered by SecurityCert</a></span> |
| 1762 | + <a class="brand-logo-link" href="#top" aria-label="Back to top"> |
| 1763 | + <img class="brand-logo-img" src="logo.png" alt="PySpector" /> |
| 1764 | + </a> |
| 1765 | + <a class="brand-sub" href="https://www.securitycert.it/" target="_blank" rel="noopener">Powered by SecurityCert</a> |
1755 | 1766 | </div> |
1756 | 1767 | <p>High-performance, graph-based static analysis security testing for modern Python projects and large codebases.</p> |
1757 | 1768 | </div> |
@@ -1783,16 +1794,17 @@ <h6>Security & community</h6> |
1783 | 1794 | </ul> |
1784 | 1795 | </div> |
1785 | 1796 | <div class="footer-col"> |
1786 | | - <h6>Contacts</h6> |
| 1797 | + <h6>Contacts & Community</h6> |
1787 | 1798 | <ul> |
1788 | 1799 | <li><a href="https://linkedin.com/showcase/pyspector/" target="_blank" rel="noopener">LinkedIn</a></li> |
| 1800 | + <li><a href="https://discord.gg/dPq2N34hts" target="_blank" rel="noopener">Discord</a></li> |
1789 | 1801 | <li><a href="mailto:pyspector@biz.securitycert.it">Email Us</a></li> |
1790 | 1802 | </ul> |
1791 | 1803 | </div> |
1792 | 1804 | </div> |
1793 | 1805 | <div class="footer-bottom"> |
1794 | 1806 | <span>© 2025-2026 Tommaso Bona · Apache License 2.0</span> |
1795 | | - <span class="mono">Powered by SecurityCert · Trusted by SatoriCI</span> |
| 1807 | + <span class="mono">Powered by SecurityCert</span> |
1796 | 1808 | </div> |
1797 | 1809 | </div> |
1798 | 1810 | </footer> |
@@ -1857,6 +1869,19 @@ <h6>Contacts</h6> |
1857 | 1869 | } catch (err) { console.error('Release fetch failed:', err); } |
1858 | 1870 | } |
1859 | 1871 |
|
| 1872 | +// ---------- GitHub live star count ---------- |
| 1873 | +async function fetchGithubStars() { |
| 1874 | + try { |
| 1875 | + const res = await fetch('https://api.github.com/repos/ParzivalHack/PySpector'); |
| 1876 | + if (!res.ok) return; |
| 1877 | + const data = await res.json(); |
| 1878 | + const el = document.getElementById('star-count'); |
| 1879 | + if (el && typeof data.stargazers_count === 'number') { |
| 1880 | + el.textContent = data.stargazers_count.toLocaleString('en-US'); |
| 1881 | + } |
| 1882 | + } catch (err) { console.error('Star count fetch failed:', err); } |
| 1883 | +} |
| 1884 | + |
1860 | 1885 | // ---------- Taint path geometry (hero) ---------- |
1861 | 1886 | function drawTaintPath() { |
1862 | 1887 | const src = document.getElementById('trace-src'); |
@@ -1955,6 +1980,7 @@ <h6>Contacts</h6> |
1955 | 1980 |
|
1956 | 1981 | document.addEventListener('DOMContentLoaded', () => { |
1957 | 1982 | fetchLatestRelease(); |
| 1983 | + fetchGithubStars(); |
1958 | 1984 | drawTaintPath(); |
1959 | 1985 | setupScrollSpy(); |
1960 | 1986 | setupReveals(); |
|
0 commit comments