commit 0bb73dd390f7b9abda533aaa5f43497abd4253dd Author: Christian Date: Mon Jun 8 20:37:32 2026 -0400 Add files via upload diff --git a/download.html b/download.html new file mode 100644 index 0000000..85340e3 --- /dev/null +++ b/download.html @@ -0,0 +1,159 @@ + + + + + + Download Portage + + + + + + + + + +
+
+

Install Portage

+

Download the latest release directly from GitHub.

+
+
+ +
+
+
+ +
+
+ +
+

Chrome

+
Fetching latest...
+
+
+ Download for Chrome + +
+ + Loaded as an unpacked extension. Data swap APIs are fully supported. +
+ +
    +
  • Unzip the downloaded portage_chrome.zip file.
  • +
  • Navigate to chrome://extensions in your URL bar.
  • +
  • Enable Developer mode in the top right corner.
  • +
  • Click Load unpacked and select the unzipped folder.
  • +
+
+ +
+
+ +
+

Firefox

+
Fetching latest...
+
+
+ Download for Firefox + +
+ + Loaded as a temporary add-on. History titles and times are preserved. +
+ +
    +
  • Unzip the downloaded portage_firefox.zip file.
  • +
  • Navigate to about:debugging#/runtime/this-firefox.
  • +
  • Click Load Temporary Add-on...
  • +
  • Select the manifest.json file inside the unzipped folder.
  • +
+
+ +
+ + +
+
+ + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..634580c --- /dev/null +++ b/index.html @@ -0,0 +1,211 @@ + + + + + + + Portage — A universal browser data swap + + + + + + + + + + + + +
+
+
+ Open source · v2.0 · Chrome & Firefox +

Carry your browser data anywhere.

+

Portage gathers your bookmarks, history, cookies, tabs, sessions and reading list into one encrypted, portable file — then rebuilds it in any browser. Chrome → Firefox, Firefox → Chrome, or the same browser on a new machine.

+ +
+ AES-256 encrypted + 100% local + Both directions + No tracking +
+ +
+
portage · export
+ Portage export screen showing data categories +
+
+
+ +
+
+
+
Why Portage
+

Everything that matters, nothing that doesn't

+

Built on the official browser APIs — so it only ever touches data it can actually move, and never pretends otherwise.

+
+
+
+
+

Universal swap

+

It's just JSON, so it doesn't care who made it. Chrome ↔ Firefox, or the same browser on a new machine, profile or reinstall.

+
+
+
+

Top-tier encryption

+

Optional AES-256-GCM with PBKDF2 key derivation. Your bundle — cookies and all — is unreadable on disk without your passphrase.

+
+
+
+

The whole picture

+

Bookmarks, history, cookies, open tabs, recently closed sessions and your reading list — recreated faithfully on the other side.

+
+
+
+

Reinstall checklist

+

No API can reinstall extensions, so Portage exports them as a tidy .txt with store-search links for both browsers — perfect for an AI assistant.

+
+
+
+

Themeable, readable

+

Monochrome by default, with light/dark and any accent colour you like. The palette auto-adjusts to keep text crisp and legible.

+
+
+
+

Private by design

+

Nothing is uploaded, ever. Data lives only in the page while you work and is wiped from memory the moment you export or import.

+
+
+
+
+ +
+
+
+
A closer look
+

Designed to feel effortless

+

Animated progress, live counts and honest status on every category — so you always know exactly what's moving.

+
+
+
+
+

Scan & export

+

One click scans your browser and lays out everything Portage can carry. Toggle off anything you'd rather leave behind, encrypt it, and save a single portable file.

+
Live progressBundle sizeSelective export
+
+
+
portage · export
+ Portage export screen +
+
+
+
+

Drop & import

+

Open Portage in the destination browser, drop the file in, and it recreates your data through the official APIs. Encrypted bundles simply ask for your passphrase first.

+
Drag & dropPer-category statusImported / skipped tally
+
+
+
portage · import
+ Portage import screen +
+
+
+
+

Right from your toolbar

+

A compact popup shows live counts at a glance and opens the full workspace in a click. Themed to match the rest of Portage, in light or dark.

+
Quick countsOne-click open
+
+
+
popup
+ Portage toolbar popup +
+
+
+
+
+ +
+
+
+
Honest by default
+

What actually transfers

+

Extensions can only use the browser APIs — so this is everything Portage moves, and exactly how.

+
+
+ + + + + + + + + + + + +
DataExportOn import
BookmarksFull treeRecreated in a “Portage Import” folder
HistoryYesRe-added — Firefox keeps titles & times
CookiesYesRe-set, so logins carry over
Open tabsYesReopened in the window
Recently closedYesReopened as tabs
Reading listYesChrome → reading list · Firefox → bookmark folder
ExtensionsListChecklist only — no API can reinstall them
Passwords & autofillNo browser API exists — use the built-in export
+
+
+
+ +
+
+
+
+

Ready to move your data?

+

Free, open source, and entirely on your machine. Install Portage in under a minute.

+ +
+
+
+ + + + diff --git a/site.js b/site.js new file mode 100644 index 0000000..badbacd --- /dev/null +++ b/site.js @@ -0,0 +1,39 @@ +/* +Portage by pvrz +https://github.com/pvrzz/Portage/ +File Name: site.js +*/ + +(function () { + const KEY = "portage-site-theme"; + const root = document.documentElement; + const SUN = ''; + const MOON = ''; + + function current() { try { return localStorage.getItem(KEY) || "system"; } catch { return "system"; } } + function effective() { const m = current(); return m !== "system" ? m : (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); } + function setMode(m) { if (m === "system") root.removeAttribute("data-mode"); else root.setAttribute("data-mode", m); try { localStorage.setItem(KEY, m); } catch {} updateIcon(); } + function updateIcon() { const b = document.getElementById("themeToggle"); if (b) b.innerHTML = effective() === "dark" ? SUN : MOON; } + + const init = current(); + if (init !== "system") root.setAttribute("data-mode", init); + + document.addEventListener("DOMContentLoaded", () => { + updateIcon(); + const toggle = document.getElementById("themeToggle"); + if (toggle) toggle.addEventListener("click", () => setMode(effective() === "dark" ? "light" : "dark")); + + const nav = document.querySelector(".nav"); + const onScroll = () => { if (nav) nav.classList.toggle("scrolled", window.scrollY > 8); }; + onScroll(); + window.addEventListener("scroll", onScroll, { passive: true }); + + const obs = new IntersectionObserver((entries) => { + entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); obs.unobserve(e.target); } }); + }, { threshold: 0.12 }); + document.querySelectorAll(".reveal").forEach((el) => obs.observe(el)); + + const y = document.getElementById("year"); + if (y) y.textContent = new Date().getFullYear(); + }); +})(); diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..f291da8 --- /dev/null +++ b/styles.css @@ -0,0 +1,213 @@ +/* +Portage by pvrz +https://github.com/pvrzz/Portage/ +File Name: styles.css +*/ + +:root { + --ink-900: #18181b; + --ink-800: #27272a; + --ink-700: #3f3f46; + --ink-600: #52525b; + --ink-500: #71717a; + --ink-400: #a1a1aa; + --ink-300: #d4d4d8; + --ink-200: #e4e4e7; + --ink-100: #f4f4f5; + --paper: #ffffff; + --bg: #fafafa; + --line: #e7e7ea; + --accent: #18181b; + --accent-contrast: #fafafa; + --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; + --mono: ui-monospace, "Cascadia Code", "SF Mono", Menlo, Consolas, monospace; + --radius: 14px; + --radius-sm: 9px; + --shadow: 0 1px 2px rgba(24,24,27,.04), 0 12px 32px rgba(24,24,27,.08); + --shadow-lg: 0 30px 70px rgba(24,24,27,.16); + --ease: cubic-bezier(.22,.61,.36,1); + --maxw: 1120px; +} + +:root[data-mode="dark"] { + --ink-900: #fafafa; --ink-800: #f4f4f5; --ink-700: #e4e4e7; --ink-600: #d4d4d8; + --ink-500: #a1a1aa; --ink-400: #71717a; --ink-300: #52525b; --ink-200: #3f3f46; --ink-100: #27272a; + --paper: #1c1c1f; --bg: #131316; --line: #2a2a2e; + --accent: #fafafa; --accent-contrast: #18181b; + --shadow: 0 1px 2px rgba(0,0,0,.4), 0 12px 32px rgba(0,0,0,.5); + --shadow-lg: 0 30px 70px rgba(0,0,0,.6); +} +@media (prefers-color-scheme: dark) { + :root:not([data-mode]) { + --ink-900: #fafafa; --ink-800: #f4f4f5; --ink-700: #e4e4e7; --ink-600: #d4d4d8; + --ink-500: #a1a1aa; --ink-400: #71717a; --ink-300: #52525b; --ink-200: #3f3f46; --ink-100: #27272a; + --paper: #1c1c1f; --bg: #131316; --line: #2a2a2e; + --accent: #fafafa; --accent-contrast: #18181b; + --shadow: 0 1px 2px rgba(0,0,0,.4), 0 12px 32px rgba(0,0,0,.5); + --shadow-lg: 0 30px 70px rgba(0,0,0,.6); + } +} + +* { box-sizing: border-box; margin: 0; padding: 0; } +html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; } +body { + font-family: var(--font); background: var(--bg); color: var(--ink-900); + line-height: 1.6; letter-spacing: -0.01em; overflow-x: hidden; + transition: background .3s var(--ease), color .3s var(--ease); +} +a { color: inherit; text-decoration: none; } +::selection { background: var(--accent); color: var(--accent-contrast); } +.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; } +.mono { font-family: var(--mono); } + +.nav { + position: sticky; top: 0; z-index: 50; + background: color-mix(in srgb, var(--bg) 82%, transparent); + backdrop-filter: blur(12px); border-bottom: 1px solid transparent; + transition: border-color .3s, background .3s; +} +.nav.scrolled { border-color: var(--line); } +.nav-inner { max-width: var(--maxw); margin: 0 auto; padding: 14px 24px; display: flex; align-items: center; gap: 16px; } +.brand { display: flex; align-items: center; gap: 11px; font-weight: 700; font-size: 17px; letter-spacing: -0.02em; } +.brand .mk { width: 34px; height: 34px; border-radius: 9px; background: var(--accent); color: var(--accent-contrast); display: grid; place-items: center; } +.brand .mk svg { width: 20px; height: 20px; } +.nav .links { display: flex; gap: 4px; margin-left: auto; align-items: center; } +.nav .links a { padding: 8px 14px; border-radius: 8px; font-size: 14px; font-weight: 500; color: var(--ink-600); transition: color .15s, background .15s; } +.nav .links a:hover { color: var(--ink-900); background: var(--ink-100); } +.nav .links a.cta { background: var(--accent); color: var(--accent-contrast); } +.nav .links a.cta:hover { filter: brightness(.92); background: var(--accent); } +.theme-toggle { + width: 38px; height: 38px; border-radius: 9px; border: 1px solid var(--line); + background: transparent; color: var(--ink-700); cursor: pointer; display: grid; place-items: center; transition: background .15s, color .15s; +} +.theme-toggle:hover { background: var(--ink-100); color: var(--ink-900); } +.theme-toggle svg { width: 18px; height: 18px; } +@media (max-width: 720px) { .nav .links a.navlink { display: none; } } + +.btn { + display: inline-flex; align-items: center; justify-content: center; gap: 9px; + font: inherit; font-weight: 600; font-size: 15px; padding: 13px 22px; border-radius: 10px; + border: 1px solid transparent; cursor: pointer; transition: transform .12s var(--ease), filter .15s, background .15s; white-space: nowrap; +} +.btn:active { transform: scale(.97); } +.btn svg { width: 18px; height: 18px; } +.btn-primary { background: var(--accent); color: var(--accent-contrast); } +.btn-primary:hover { filter: brightness(.92); } +.btn-ghost { background: transparent; color: var(--ink-800); border-color: var(--line); } +.btn-ghost:hover { background: var(--ink-100); } +.btn-lg { padding: 15px 28px; font-size: 16px; } + +.hero { position: relative; padding: 80px 0 40px; text-align: center; } +.hero .eyebrow { + display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--ink-600); + background: var(--paper); border: 1px solid var(--line); padding: 7px 14px; border-radius: 99px; margin-bottom: 26px; box-shadow: var(--shadow); +} +.hero .eyebrow .dot { width: 7px; height: 7px; border-radius: 99px; background: var(--accent); } +.hero h1 { font-size: clamp(38px, 7vw, 76px); line-height: 1.02; letter-spacing: -0.04em; font-weight: 750; } +.hero h1 .grad { background: linear-gradient(120deg, var(--ink-900), var(--ink-500)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; } +.hero p.lead { font-size: clamp(16px, 2.4vw, 20px); color: var(--ink-600); max-width: 620px; margin: 24px auto 0; } +.hero .cta-row { display: flex; gap: 12px; justify-content: center; margin-top: 34px; flex-wrap: wrap; } +.hero .trust { margin-top: 30px; display: flex; gap: 22px; justify-content: center; flex-wrap: wrap; color: var(--ink-500); font-size: 13.5px; } +.hero .trust span { display: inline-flex; align-items: center; gap: 7px; } +.hero .trust svg { width: 15px; height: 15px; } + +.frame { + position: relative; margin: 56px auto 0; max-width: 980px; border-radius: 16px; + background: var(--paper); border: 1px solid var(--line); box-shadow: var(--shadow-lg); overflow: hidden; +} +.frame .bar { display: flex; align-items: center; gap: 7px; padding: 12px 16px; border-bottom: 1px solid var(--line); background: var(--bg); } +.frame .bar i { width: 11px; height: 11px; border-radius: 99px; background: var(--ink-300); display: inline-block; } +.frame .bar .addr { margin-left: 12px; font-size: 12px; color: var(--ink-500); font-family: var(--mono); } +.frame img { display: block; width: 100%; height: auto; } +.glow { position: absolute; inset: -10% 10% auto 10%; height: 320px; background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 18%, transparent), transparent); filter: blur(30px); z-index: -1; } + +section { padding: 70px 0; } +.section-head { text-align: center; max-width: 640px; margin: 0 auto 48px; } +.section-head .tag { font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; color: var(--ink-500); } +.section-head h2 { font-size: clamp(28px, 4.5vw, 44px); letter-spacing: -0.03em; margin-top: 12px; font-weight: 720; } +.section-head p { color: var(--ink-600); margin-top: 14px; font-size: 16px; } + +.features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; } +.feature { + background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); padding: 26px; + transition: transform .25s var(--ease), box-shadow .25s var(--ease), border-color .25s; +} +.feature:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: var(--ink-300); } +.feature .ic { width: 46px; height: 46px; border-radius: 12px; background: var(--accent); color: var(--accent-contrast); display: grid; place-items: center; margin-bottom: 18px; } +.feature .ic svg { width: 23px; height: 23px; } +.feature h3 { font-size: 18px; letter-spacing: -0.02em; } +.feature p { color: var(--ink-600); font-size: 14.5px; margin-top: 8px; } +@media (max-width: 860px) { .features { grid-template-columns: 1fr 1fr; } } +@media (max-width: 560px) { .features { grid-template-columns: 1fr; } } + +.shots { display: grid; gap: 30px; } +.shot { display: grid; grid-template-columns: 1.4fr 1fr; gap: 36px; align-items: center; } +.shot:nth-child(even) .shot-copy { order: 2; } +.shot-copy h3 { font-size: 24px; letter-spacing: -0.02em; } +.shot-copy p { color: var(--ink-600); margin-top: 10px; font-size: 15px; } +.shot-copy .chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; } +.chip { font-size: 12.5px; font-weight: 600; color: var(--ink-600); background: var(--ink-100); border: 1px solid var(--line); padding: 5px 11px; border-radius: 99px; } +.shot .frame { margin: 0; max-width: none; } +@media (max-width: 820px) { .shot { grid-template-columns: 1fr; } .shot:nth-child(even) .shot-copy { order: 0; } } + +.table-card { background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); } +table.matrix { width: 100%; border-collapse: collapse; font-size: 14.5px; } +table.matrix th, table.matrix td { padding: 15px 20px; text-align: left; border-bottom: 1px solid var(--line); } +table.matrix thead th { font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-500); background: var(--bg); } +table.matrix tbody tr:last-child td { border-bottom: 0; } +table.matrix td.c { text-align: center; width: 130px; } +table.matrix .yes { color: var(--ink-900); font-weight: 600; } +table.matrix .no { color: var(--ink-400); } +table.matrix td:first-child { font-weight: 600; } + +.cta-band { background: var(--paper); border: 1px solid var(--line); border-radius: 22px; padding: 56px 32px; text-align: center; box-shadow: var(--shadow); position: relative; overflow: hidden; } +.cta-band h2 { font-size: clamp(26px, 4vw, 40px); letter-spacing: -0.03em; } +.cta-band p { color: var(--ink-600); margin: 12px auto 26px; max-width: 480px; } +.cta-band .cta-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; } + +.dl-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; margin-top: 10px; } +@media (max-width: 760px) { .dl-grid { grid-template-columns: 1fr; } } +.dl-card { background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius); padding: 30px; box-shadow: var(--shadow); } +.dl-card .top { display: flex; align-items: center; gap: 14px; margin-bottom: 8px; } +.dl-card .logo { width: 46px; height: 46px; flex: none; } +.dl-card .logo svg { width: 46px; height: 46px; } +.dl-card h3 { font-size: 20px; } +.dl-card .ver { font-size: 12.5px; color: var(--ink-500); } +.dl-card .btn { width: 100%; margin-top: 18px; } +.steps { margin-top: 22px; counter-reset: s; display: grid; gap: 14px; } +.steps li { list-style: none; display: flex; gap: 13px; font-size: 14px; color: var(--ink-700); } +.steps li::before { counter-increment: s; content: counter(s); flex: none; width: 24px; height: 24px; border-radius: 99px; background: var(--accent); color: var(--accent-contrast); font-size: 12px; font-weight: 700; display: grid; place-items: center; } +.steps code { font-family: var(--mono); font-size: 12.5px; background: var(--ink-100); padding: 1px 6px; border-radius: 5px; } +.note { display: flex; gap: 11px; align-items: flex-start; background: var(--ink-100); border-radius: var(--radius-sm); padding: 15px 17px; font-size: 13.5px; color: var(--ink-600); margin-top: 26px; } +.note svg { width: 18px; height: 18px; flex: none; margin-top: 1px; stroke: var(--ink-500); } + +.page-head { text-align: center; padding: 64px 0 8px; } +.page-head h1 { font-size: clamp(34px, 6vw, 56px); letter-spacing: -0.03em; } +.page-head p { color: var(--ink-600); margin-top: 14px; font-size: 17px; } + +.star-band { text-align: center; margin-top: 40px; } +.ghstar { display: inline-flex; align-items: center; gap: 10px; border: 1px solid var(--line); background: var(--paper); padding: 12px 20px; border-radius: 11px; font-weight: 600; font-size: 15px; box-shadow: var(--shadow); transition: transform .15s, border-color .15s; } +.ghstar:hover { transform: translateY(-2px); border-color: var(--ink-400); } +.ghstar svg { width: 19px; height: 19px; } + +.foot { border-top: 1px solid var(--line); margin-top: 50px; padding: 40px 0; } +.foot-inner { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; display: flex; align-items: center; gap: 18px; flex-wrap: wrap; } +.foot .brand { font-size: 15px; } +.foot .spacer { flex: 1; } +.foot .links2 { display: flex; gap: 18px; font-size: 14px; color: var(--ink-600); } +.foot .links2 a:hover { color: var(--ink-900); } +.foot .made { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-500); font-size: 14px; } +.foot .made a { color: var(--ink-700); font-weight: 600; } +.foot .made .heart { color: var(--accent); display: inline-flex; } +.foot .made .heart svg { width: 14px; height: 14px; animation: beat 1.4s var(--ease) infinite; } +@keyframes beat { 0%,100% { transform: scale(1); } 15% { transform: scale(1.25); } 30% { transform: scale(1); } } + +.reveal { opacity: 0; transform: translateY(22px); transition: opacity .7s var(--ease), transform .7s var(--ease); } +.reveal.in { opacity: 1; transform: none; } +.float { animation: floaty 6s ease-in-out infinite; } +@keyframes floaty { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } + +@media (prefers-reduced-motion: reduce) { + * { animation-duration: .001s !important; transition-duration: .001s !important; } + html { scroll-behavior: auto; } +}