diff --git a/README.md b/README.md index 2e98f66..4a7554e 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ This repository is a place for collaborative design and design suggestions. ## Current revision -`index.html` is `Profile Page + Overview.dc.html`, the 2026-08-27 transfer from -Claude Design ("Alignment Asset Site - Ready for Dev"), byte-for-byte. Do not -hand-edit it — re-export from Design Composer and re-copy instead. +`index.html` started as `Profile Page + Overview.dc.html` (2026-08-27 Claude +Design transfer). Branch `lukas/contribution-profile-ia` is a hand-edited +working copy of that prototype — see `changelog.md` §7–§9. See `changelog.md` for the design decisions behind this revision, and `CLAUDE.md` for the copy rule (PLAA is **collected**, never "earned"). diff --git a/changelog.md b/changelog.md index d4da8a3..57fe2d3 100644 --- a/changelog.md +++ b/changelog.md @@ -95,6 +95,241 @@ side, each with its own "Confirmed by Surus" line. --- +## 7. Lukas pass — contribution profile + IA (2026-09-08) + +Working copy on `lukas/contribution-profile-ia` (`eca16fc`). This is the first +hand-edit of the 2026-08-27 Design Composer transfer. Home and Profile had +become two signed-in surfaces doing the same job; points and PLAA were mixed +on the same axes and columns; "snapshot" was the word for a calendar month. +The pass collapses the IA and makes the contribution profile PLAA-only. + +### Information architecture + +Previously the rail included Home, Profile, Overview, Activities, +Leaderboard, FAQ, Portfolio, and Kudos. Home and Profile both claimed to +be the member's starting point. + +- **Home is off the rail.** Profile is the signed-in default. Prospects still + land on Overview (the old marketing Home markup is in the file but + unreachable). **Why:** Home and Profile were duplicative; one signed-in + surface is enough. +- **Overview absorbs FAQ and Portfolio & Holdings** behind a top toggle + (Introduction to PLAA / FAQ / Portfolio & Holdings). **Why:** those three + were sibling tabs for the same "what is PLAA / what does the trust hold" + question; a toggle keeps the rail short. +- **Leaderboard moves under Activities** as a third toggle (Activities / + Incentivized Network Asks / Leaderboard). Clicking the Activities rail + always opens Activities, not whichever toggle was last. **Why:** ranking + is a view of activity, not a destination of its own. +- **Activity tiers collapse by default** (Start here / Build momentum / + High-leverage). **Why:** the list was a wall of cards before you could + pick a thing to do. + +### Contribution table + +Previously the table mixed a points column with PLAA columns, called the +month a "snapshot", labelled the second stream "Infra rewards", and treated +Balance as a peer of Activities rather than a running total. + +- **Snapshot → Period.** The column is "Points since mmm, dd" (e.g. Jul 1, + 2026). "Snapshot" is stripped from the profile surface, not only the + header. **Why:** a snapshot is an event; this is a calendar month of + collection. +- **Table is PLAA-only.** Points never sit in their own column. Activities + show converted PLAA with the originating points as small supporting text + underneath. **Why:** mixing units in one row made every figure ambiguous. + PLAA is the answer; points are provenance. +- **Infra rewards → Rewards.** Shorter, and the Infra Member pill in the + hero already names the stream. +- **Balance → Ending balance.** It is derived from the three columns beside + it (Activities + Rewards − Redeemed), not a fourth independent number. +- **Redeemed shows dollar proceeds** at that period's clearing price, as + small supporting text under the PLAA — the same pattern as points under + Activities. +- **Column headers have short tooltips** explaining what the column is, with + a jump into Activities, Overview, or Portfolio so the header is also a + way into the related surface. +- **Click/hover an Activities, Rewards, or Redeemed cell** opens a + period-detail modal — the same job the blue-bar "snapshot summary" used + to do, attached to the number you are looking at. + +### Chart + +Previously a mixed points/PLAA series with the legend off the plot. + +- **No mixed units.** The chart is a per-period PLAA waterfall: activities + and rewards as positive stacks, redeemed as a negative stack, each + segment labeled, net under the period. **Why:** one currency on the + plot; the stack is the period's story. +- **Cumulative ending-balance line kept** on a second axis. Axis titles sit + on the plot, above each vertical axis, rather than in a distant legend. +- **Series chips** hide/show Activities, Rewards, Redeemed, and Ending + balance so you can read the chart with or without a stream. +- **Drag across periods** reports relative and absolute change for the + selected range. **Click a segment** opens that period's detail modal. + +### Copy and leftovers + +- **"Points this snapshot" is off the profile hero.** The hero is a PLAA + balance; the period's points live in the table. +- **Snapshot history is no longer a profile toggle.** It duplicated the + contribution table under the chart. +- **Copy rule still holds:** PLAA and points are collected, never earned. + The (now-unreachable) Home CTA is **Collect PLAA now**, not "Earn PLAA + now" — even though the original note asked for earn wording. Flip if + Bill wants the exception. +- **Still in the file, unused:** old Home dashboard markup (signed-in + variants) and the old Snapshot history table. Easy to restore; not on + the rail. + +--- + +## 8. Lukas pass 2 — profile / chart / table / Overview / Activities (2026-09-08) + +`4c903a9`. Pass 1 shipped a working IA; this pass is interaction and +alignment — header, chart chrome, table states, Overview as a stable +shell, Activities as the place you see the current period. + +### Profile header + +Previously the name sat inset from "Member since", a decorative verified +check sat next to it, and Network Ambassador was a static badge with no +path to the next status. + +- **Name left-aligns with "Member since".** One vertical edge for identity. +- **Verified check removed.** It had no purpose in this prototype (nothing + to verify against). +- **Network Ambassador becomes a status speedometer** toward the next + level. Invented ladder, flagged as invented: Contributor → Network + Ambassador → Steward → Fellow. Demo member (Alex) is at Ambassador, 17 + activities short of Steward. **Why:** a badge that does not show how you + unlock the next one is just decoration. Rewrite the ladder if a real + one exists. + +### Chart + +- **Drag-delta sits in the plot**, not in a strip below it. You are looking + at the range; the number should be there too. +- **Axis titles align with the tick labels** on their own axis, rather + than floating in the margin. +- **Net gain/loss sits on the stacked bar** (not as a separate caption + underneath) and **follows series toggles** — hiding Rewards recomputes + the net. **Why:** the net is a property of what is currently shown, not + of the full dataset. +- **Hover a balance dot** for the exact ending-balance number. The line + was unreadable to the ones place without it. +- Titles: **Your PLAA over time** / **Your Contribution History**. + +### Table + +- **Points only on click** (this is reversed in §9 — it made closed + periods look empty). Rewards **top-aligned** so the PLAA figures share + a baseline with Activities and Redeemed. +- **PLAA bracket over the four PLAA columns is gone.** Everything in the + table is PLAA now; grouping was leftover from when a points column sat + beside them (§4). +- **Header tooltips are click-to-open** and no longer clip at the table + edge. Hover cut them off wherever the table ended. +- **Period is `mmm, yyyy`** (e.g. Jul, 2026), not `mmm dd, yyyy`. + **Why:** every row is a whole calendar month, so the day was noise. + (Pass 1's "Points since mmm, dd" was the first try at naming the + column; the month-year form is the one that stuck.) +- **Redeemed header jumps to Redeeming PLAA** on Overview → Introduction + to PLAA — the place that explains the mechanism, not a dead tooltip. +- **Current period (Aug) shows live points; PLAA and ending balance are + blurred.** **Why:** conversion is not known until the period closes. + Showing a calculated PLAA for an open month would be a fake number. +- **Redeemed total includes dollars and average clearing price**, so the + footer answers "how much did I actually take out?" without opening a + row. + +### Overview + +- **Header stays Overview** across the toggle, same pattern as Profile. + Flipping Introduction / FAQ / Portfolio no longer retitles the page. +- **Toggle order:** Portfolio & Holdings → Introduction to PLAA → FAQ. + Holdings first, because that is what a returning member comes for; + intro and FAQ are for orientation. +- **How-it-works boxes are tabs**, not four stacked sections: + Contribute → Who it's for; Collect → Points per Period; Convert → + Points → PLAA Conversion; Redeem (was Capitalize) → Redeeming PLAA. + **Why:** Capitalize implied a financial product. Redeem is the actual + action. +- **"Collect points this snapshot" CTA box removed** from the bottom of + Introduction to PLAA. It duplicated the Activities rail and still + said snapshot. +- **NAV:** hover spells out Net Asset Value and how it is calculated; + drag reports absolute and percent delta. Mock **circulating PLAA** and + **implied $/PLAA** so NAV ÷ tokens is visible. Circulating series is + invented mock data, flagged as such. + +### Activities + +- **Current-period spider chart moved here from Leaderboard.** + **Why:** the spider is "how does this period look for me", which + belongs with the activity list, not with a ranking of other people. + +--- + +## 9. Lukas pass 3 — profile polish + NAV history (2026-09-08) + +`35ae08f`. Pass 2 hid points behind a click and clipped several pieces of +chrome. This pass restores always-on points, puts chart hover and axis +titles inside the plot, sizes the status meter to the photo, and makes +the NAV chart a dilution read over a real history. + +### Points (reverses §8) + +Pass 2 hid points except on the current period (and on click of activity +PLAA). Closed rows then looked like they had no points. + +- **Points stay visible on every period row.** They are supporting text + under Activities PLAA, always on. +- **Click the points (not the PLAA)** to open what they were collected + for. Closed periods open that period's activity list; the current + period opens the in-review summary. **Why:** the PLAA figure is the + converted result; the points are the work. Clicking the work should + show the work. + +### Chart chrome + +- **Axis titles left-align to the left axis and right-align to the right + axis**, clipped to the plot. Pass 2's titles overflowed the chart + bounds. Labels: **Period net (PLAA)** / **Ending balance (PLAA)**. +- **Hover tooltip is drawn as SVG** (the previous HTML overlay rendered + as an empty blue box). Hovering **the plot**, not only the ending- + balance dots, fills it (e.g. `Jul 1,711 PLAA`). + +### Header and table chrome + +- **Network Ambassador meter is 72px tall** — same top and bottom as the + profile photo. Pass 2's speedometer was a short pill next to a tall + photo. +- **Column-header bubbles right-align with the header text**; the caret + sits under the label, not the left edge of the cell. Headers are + right-aligned numbers, so a left-aligned bubble looked unattached. +- **Repeated page titles removed** on Overview, Activities, and Kudos. + The rail already names the tab; an in-page H1 repeating it was noise. + Profile keeps its own heading because the page is the person, not the + rail label. + +### NAV history (Overview → Portfolio & Holdings) + +Pass 2's NAV chart was a short recent window, so the two series could +not be compared and the dilution question was invisible. + +- **Range is Jan 2025–Jul 2026.** Long enough for the slopes to mean + something. +- **NAV (solid) and issued PLAA (dashed) are both indexed to 100 at + start** so the two slopes are comparable on one axis. Raw NAV in + dollars and raw issued tokens cannot share a scale. +- **Issued rising faster than NAV is the dilution signal** — implied + $/PLAA falls. That is the point of the chart, not a side effect. +- **Hover and drag report both series.** Same interaction pattern as + the profile waterfall. + +--- + ## For the doc - Length: my call — aiming at ~2 pages, decisions and rationale, no exhaustive diff --git a/docs/pr/activities.png b/docs/pr/activities.png new file mode 100644 index 0000000..20c2bcc Binary files /dev/null and b/docs/pr/activities.png differ diff --git a/docs/pr/contribution-history.png b/docs/pr/contribution-history.png new file mode 100644 index 0000000..d3bd4e5 Binary files /dev/null and b/docs/pr/contribution-history.png differ diff --git a/docs/pr/overview-nav.png b/docs/pr/overview-nav.png new file mode 100644 index 0000000..e29492e Binary files /dev/null and b/docs/pr/overview-nav.png differ diff --git a/docs/pr/overview.png b/docs/pr/overview.png new file mode 100644 index 0000000..78c1c65 Binary files /dev/null and b/docs/pr/overview.png differ diff --git a/docs/pr/profile.png b/docs/pr/profile.png new file mode 100644 index 0000000..d3bd4e5 Binary files /dev/null and b/docs/pr/profile.png differ diff --git a/index.html b/index.html index b7dcb62..41cd2d4 100644 --- a/index.html +++ b/index.html @@ -38,6 +38,17 @@ .ov-tip-bubble{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);margin-bottom:10px;width:240px;background:var(--color-brand);color:#fff;font:var(--text-body-sm);padding:8px 10px;border-radius:var(--radius-md);box-shadow:var(--shadow-md);opacity:0;visibility:hidden;transition:opacity .14s ease;pointer-events:none;z-index:50;text-wrap:pretty;} .ov-tip-bubble::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);border:5px solid transparent;border-top-color:var(--color-brand);} .ov-tip:hover .ov-tip-bubble,.ov-tip:focus-within .ov-tip-bubble{opacity:1;visibility:visible;} + .col-tip{position:relative;display:flex;align-items:center;gap:4px;justify-content:flex-end;width:100%;text-align:right;cursor:pointer;} + .col-tip .col-tip-bubble{position:absolute;bottom:calc(100% + 8px);right:0;left:auto;width:252px;background:var(--color-brand);color:#fff;font:var(--text-body-sm);padding:10px 12px;border-radius:var(--radius-md);box-shadow:var(--shadow-md);opacity:0;visibility:hidden;transition:opacity .14s ease;pointer-events:none;z-index:80;text-wrap:pretty;text-align:left;text-transform:none;letter-spacing:0;font-weight:400;white-space:normal;} + .col-tip .col-tip-bubble::after{content:"";position:absolute;top:100%;right:14px;left:auto;border:5px solid transparent;border-top-color:var(--color-brand);} + .col-tip.is-open .col-tip-bubble{opacity:1;visibility:visible;pointer-events:auto;} + .col-tip .col-tip-bubble a,.col-tip .col-tip-bubble button{color:#fff;font-weight:600;text-decoration:underline;background:none;border:none;padding:0;cursor:pointer;font:inherit;} + .hist-wrap{overflow:visible;} + .blur-num{filter:blur(5px);user-select:none;pointer-events:none;letter-spacing:0.02em;} + .status-meter{display:flex;align-items:center;gap:10px;height:72px;box-sizing:border-box;padding:8px 14px 8px 10px;border-radius:var(--radius-lg);background:linear-gradient(135deg,rgba(27,77,255,0.08),rgba(18,165,148,0.08));box-shadow:inset 0 0 0 1px rgba(27,77,255,0.16);} + .chart-chip{height:28px;padding:0 10px;border:none;border-radius:var(--radius-md);cursor:pointer;font:var(--text-label-md);font-weight:600;display:inline-flex;align-items:center;gap:6px;} + .chart-chip .sw{width:10px;height:10px;border-radius:2px;flex:none;} + .act-cat-toggle{width:100%;display:flex;align-items:center;gap:10px;margin-bottom:0;padding:0;border:none;background:transparent;cursor:pointer;text-align:left;} *:focus-visible{outline:none;box-shadow:var(--ring-focus-brand);border-radius:6px;} @media (prefers-reduced-motion: reduce){*{animation-duration:.001ms !important;}} @@ -89,7 +100,7 @@
- August 2026 snapshot + August 2026 period {{ daysLeft }} days left to contribute
@@ -97,9 +108,9 @@
{{ displayPoints }} - points collected this snapshot + points collected this period - +
@@ -369,7 +380,7 @@

{{ greeting }}<
+{{ homeLastGain }} - in the July 2026 snapshot + in the July 2026 period
@@ -382,7 +393,7 @@

{{ greeting }}<
-
{{ snapshotLabel }} snapshot
+
{{ snapshotLabel }} period
Open
Closes {{ nextConvert }} · 10 points to 1 PLAA
@@ -401,7 +412,7 @@

{{ greeting }}<
Days remaining

- +

@@ -462,7 +473,7 @@

{{ greeting }}<
-
Collect more points this snapshot
+
Collect more points this period
Picked from the categories you have contributed to least
@@ -493,18 +504,89 @@

{{ greeting }}<
-

Activities

-
+
-

Contributions that strengthen the network, organized by effort. Points are weighted by how lightly a category is contributed this snapshot — support an open category and your share goes further.

+

Contributions that strengthen the network, organized by effort. Points are weighted by how lightly a category is contributed this period — support an open category and your share goes further.

Category1.4× - A green tag means that category is lightly contributed this snapshot — your points there go further. + A green tag means that category is lightly contributed this period — your points there go further. +
+
+ + +
+
+
+
+

{{ lbSnapLabel }} period

+ + Live + +
+

{{ lbSnapPeriod }}

+
+
+ + {{ lbSnapPosition }} + +
+
+
+ +
+
{{ m.label }}
+
+ {{ m.value }} + {{ m.delta }} +
+
{{ lbSnapVsLabel }}
+
+
+
+
+

Most popular activity categories

+

Each category gets a fixed share of the period's PLAA reward pool, so when fewer people contribute to a category, every contribution there collects a larger slice. Learn more about our incentive model. 

+

 Starred, green categories are underutilized, so points make a bigger impact.

+
+ + + + + + + + + + + + + + + + + + {{ lbCatLabelEls }} + +
+
Underutilized — boost these
+
+ +
+ +
+
{{ u.name }}
+
{{ u.hint }} · {{ u.pts }} pts
+
+ +
+
+
+
@@ -513,11 +595,13 @@

Activities

-
-
- -
Start here
Atomic, mostly auto-tracked, immediate feedback.
-
+
+ +
@@ -537,15 +621,18 @@

Activities

+
-
-
- -
Build momentum
Recurring contributions that keep the network humming.
-
+
+ +
@@ -565,15 +652,18 @@

Activities

+
-
- -
High-leverage
Intros, writing, case studies and program design — deep network impact.
-
+ +
@@ -593,6 +683,7 @@

Activities

+
@@ -656,6 +747,51 @@

Activities

+ + +
+
+

Top contributors recognized for the work that strengthens the Protocol Labs Network.

+
+ +
+
+
+
+ + {{ lbCaption }} +
+
+ # + Contributor + Top category + Points + Change +
+ +
+ {{ m.rank }} + {{ m.initials }} +
{{ m.name }}
{{ m.team }}
+ {{ m.cat }} + {{ m.ptsLabel }} + {{ m.deltaLabel }} +
+
+
+ + +
+ +
+

The network grows because people contribute.

+

Every introduction, tool and shared lesson compounds — more contribution means a stronger network, and a stronger network rewards every contributor. Find your next way to climb the leaderboard.

+
+ +
+ +
+
@@ -676,23 +812,31 @@

-
+
AR
-

Alex Rivera

Network Ambassador
-
 Member since Jan 2025, USA
+
+
+

Alex Rivera

+
Member since Jan 2025, USA
+
+
+ +
+
{{ statusLevel }}
+
{{ statusNextHint }}
+
+
+
Onboarded Infra Member
- -
-
{{ displayPoints }}
-
Points this snapshot
-
-
@@ -725,7 +869,7 @@

Activities {{ heroActivities }} - Infra rewards + Rewards {{ heroInfra }} @@ -739,11 +883,7 @@

-
- -
- - +
SnapshotActivitiesCategoriesPoints collectedPLAA @@ -776,7 +916,7 @@

Infra Member - Infra rewards, granted in PLAA + Rewards, granted in PLAA +{{ h.infra }} PLAA
@@ -795,67 +935,88 @@

- +
-

Points and PLAA balance over time

-

Points collected in each snapshot, and your PLAA balance after each close.

+

Your PLAA over time

+

Period waterfall in PLAA — activities, rewards, and redemptions — with ending balance.

-
- Points collected - PLAA balance +
+ + +
- + - - - - + - - - + + + + + + + + + + + + {{ chartLabelEls }} + + + {{ chartBrushCaption }} + -
- Points per snapshot - PLAA balance -
-
-

Contribution History

-

Every closed snapshot since you joined.

-
-
- - PLAA -
-
- SnapshotPointsActivitiesInfra rewardsRedeemedBalance +

Your Contribution History

+

Every closed period since you joined. Figures in PLAA; points always shown — click them for the activities they came from.

+
+
+ Period + Activities + PLAA collected from verified network activities this period, converted from points at close.

+
+ Rewards + PLAA granted directly to Infra Members. Rewards never pass through points.

+
+ Redeemed + PLAA sold back to the Trust at the published clearing price for that period.

+
+ Ending balance + Running PLAA held after this period's additions and redemptions. +
-
+
{{ p.period }} - {{ p.points }} - {{ p.plaa }} - {{ p.infra }} - {{ p.redeemed }} - {{ p.cum }} + + {{ p.plaa }} + {{ p.pointsSub }} + + {{ p.infra }} + + {{ p.redeemed }} + {{ p.redeemUsd }} + + {{ p.cum }}
-
+
Total to date - {{ snapTotalLabel }} {{ plaaActivitiesTotal }} {{ plaaInfraTotal }} - {{ plaaRedeemedTotal }} + + {{ plaaRedeemedTotal }} + {{ plaaRedeemedUsdTotal }} + {{ plaaBalanceTotal }}
@@ -893,8 +1054,7 @@

Contribution History<
-

Community Kudos

-

Recognize a peer's contribution this round. Spend points from your community pool — every kudos is awarded immediately and shows up on the shared board below.

+

Recognize a peer's contribution this round. Spend points from your community pool — every kudos is awarded immediately and shows up on the shared board below.

@@ -941,8 +1101,13 @@

-
-

Portfolio & Holdings

Transparency into the Trust that backs PLAA, and your personal record of PLAA collected.

+
+
+
+ +
+

Transparency into the Trust that backs PLAA, and your personal record of PLAA collected.

+
@@ -957,15 +1122,40 @@

-
Total Trust NAV
- +4.1% (90d) +
+
Total Trust NAV
+ Net Asset Value — Trust assets minus liabilities. Implied PLAA value ≈ NAV ÷ circulating PLAA. +
+ {{ navDelta90 }} +
+
{{ navLatest }}
+
+ {{ circLatest }} PLAA in circulation + Implied ≈ {{ impliedLatest }} / PLAA
-
$6.74M
+ - - + + + + + + + {{ navLabelEls }} + + + {{ navBrushCaption }} + -
Mar 2026Jun 2026
+
+ {{ navFirstPeriod }} + + NAV + Issued PLAA + + {{ navLastPeriod }} +
+
Both series indexed to 100 at start so slopes are comparable. Issued PLAA rising faster than NAV is dilution — implied value per PLAA falls.

Composition

@@ -1040,8 +1230,10 @@

Disclaimer

-

FAQ

-

Everything about contributing, points, PLAA, and eligibility — in one place.

+
+ +
+

Everything about contributing, points, PLAA, and eligibility — in one place.

@@ -1070,8 +1262,10 @@

FAQ

-

Overview

-

The Protocol Labs Alignment Asset (PLAA) refers to both PLAA1 tokens and contractual rights to those tokens, redeemable through periodic buyback auctions. Whether you have rights or tokens, the experience is the same, so we just call it all PLAA.

+
+ +
+

The Protocol Labs Alignment Asset (PLAA) refers to both PLAA1 tokens and contractual rights to those tokens, redeemable through periodic buyback auctions. Whether you have rights or tokens, the experience is the same, so we just call it all PLAA.

@@ -1092,45 +1286,21 @@

Overview

How It Works

-
-
- 1 -
Contribute
-
-
Share knowledge, make connections, and support the network.
- -
-
-
- 2 -
Collect
-
-
Collect points for your contributions each snapshot period.
- -
-
-
- 3 -
Convert
-
-
Points are converted into PLAA each month.
- -
-
-
- 4 -
Capitalize
+ +
+
+ {{ h.n }} +
{{ h.title }}
+
+
{{ h.body }}
+
-
Buyback auctions may turn PLAA into realized value.
- -
+
+

Who It’s For

@@ -1186,6 +1356,7 @@

Who It’s For

+
@@ -1198,7 +1369,10 @@

Who It’s For

Gain exposure to the network’s growth.

-

Onboarding to the PL Alignment Asset is handled through Surus, our institutional-grade compliance and custody platform. This page outlines the onboarding steps and technical requirements. Once verified, you will have the foundation to collect PLAA based on your contributions to the network.

+
+ +
+

Onboarding to the PL Alignment Asset is handled through Surus, our institutional-grade compliance and custody platform. This page outlines the onboarding steps and technical requirements. Once verified, you will have the foundation to collect PLAA based on your contributions to the network.

@@ -1370,7 +1544,7 @@

How It Works

4 -
Capitalize
+
Redeem
Buyback auctions may turn PLAA into realized value.
@@ -1428,9 +1602,10 @@

Unlock your path to PLAA. +
-

Snapshots & Points

+

Points per Period

Each monthly snapshot period, a pool of 10,000 PLAA is available to reward verified Incentivized Activities. Each category has a fixed allocation, and your points as a share of that category’s total decide how much converts to you.

@@ -1537,9 +1712,11 @@

Snapshots & Points

+ +

Points → PLAA Conversion

@@ -1603,13 +1780,11 @@

Points → PLAA Conversion

Only whole PLAA are issued. Calculated amounts are rounded down to the nearest whole PLAA, applied consistently to all participants. If your points don’t add up to at least one PLAA in a category, you won’t collect PLAA that snapshot period.

The points you collect are not guaranteed to convert into PLAA and may never result in any value. The points are a way of measuring the activities that you are doing in the network. Additionally, unless you sign the applicable PLAA documentation, you may not receive any PLAA. Any PLAA that are issued may be settled through the PLAA settlement process.
- - -
-
+
-
+ +

Redeeming PLAA

PLAA is redeemed through periodic buyback auctions run by the Trust. Bids are collected as a batch, settled at a single clearing price, and paid out in cash.

@@ -1641,14 +1816,7 @@

Redeeming PLAA

For details on eligibility, auctions, and settlement, see the FAQ.
- -
-
-

Collect points this snapshot.

-

Browse the incentivized activities open now and see what each one is worth.

-
- -
+
Nothing in these materials constitutes investment, financial, or legal advice. The clearing price established in any auction reflects the supply and demand among participating holders of PLAA and should not be construed as an appraisal, or fair market value determination of PLAA. Holders of PLAA should consult with their own financial, tax, and legal advisors before deciding whether to participate. The Alignment Asset is still in private beta, and we’re actively experimenting.
@@ -1667,7 +1835,7 @@

Collect points this snapshot - +
@@ -1718,7 +1886,7 @@

The network grows because pe
-

{{ lbSnapLabel }} snapshot

+

{{ lbSnapLabel }} period

Live @@ -1726,9 +1894,9 @@

{{ lbSnapLabel }} snapshot

{{ lbSnapPeriod }}

- + {{ lbSnapPosition }} - +
@@ -1745,7 +1913,7 @@

{{ lbSnapLabel }} snapshot

Most popular activity categories

-

Each category gets a fixed share of the snapshot's PLAA reward pool, so when fewer people contribute to a category, every contribution there earns a larger slice. Learn more about our incentive model. 

+

Each category gets a fixed share of the period's PLAA reward pool, so when fewer people contribute to a category, every contribution there collects a larger slice. Learn more about our incentive model. 

 Starred, green categories are underutilized, so points make a bigger impact.

@@ -2234,6 +2402,46 @@

+ + +
+
+
+
{{ pdKicker }}
+
{{ pdTitle }}
+ +
+
+

{{ pdLead }}

+
+
{{ pdActs }}
activities
+
{{ pdPlaa }}
activities PLAA
+
{{ pdInfra }}
rewards
+
{{ pdNet }}
net PLAA
+
+
+
{{ pdListTitle }}
+ +
+ {{ it.cat }} + {{ it.title }} + {{ it.amount }} +
+
+
+ {{ pdFootLabel }} + {{ pdFootValue }} +
+
+ +
{{ pdRedeemNote }}
+
+ +
+
+
+
+
@@ -2481,6 +2689,16 @@

Give Community Kudos

persona: 'active', points: 1240, homeVariant: 'a', + ovTab: 'intro', + howStep: 'contribute', + chartHidden: {}, + chartDrag: null, + chartHover: null, + navDrag: null, + navHover: null, + colTip: null, + periodDetail: null, + actOpen: {}, onbCountry: 'United States', // onboarding onbStep: 0, @@ -2497,7 +2715,7 @@

Give Community Kudos

submitted: {}, receipt: null, // tabs - profileTab: 'snapshots', portfolioTab: 'trust', lbTab: 'current', + profileTab: 'contribution', portfolioTab: 'trust', lbTab: 'current', kudosModalOpen: false, kRecipient: '', kAmount: 10, kMessage: '', faqQuery: '', faqOpen: 'q1', showClose: false, openSnap: null, @@ -2660,11 +2878,20 @@

Give Community Kudos

if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(url).then(done, done); else done(); } - navTo(v){ this.setState({view:v, activeId:null}); window.scrollTo(0,0); } + navTo(v){ + const patch = {view:v, activeId:null}; + if (v === 'home') { patch.view = this.state.persona === 'prospect' ? 'overview' : 'profile'; } + if (v === 'faq') { patch.view = 'overview'; patch.ovTab = 'faq'; } + if (v === 'portfolio') { patch.view = 'overview'; patch.ovTab = 'portfolio'; } + if (v === 'leaderboard') { patch.view = 'activities'; patch.actTab = 'leaderboard'; } + if (v === 'overview' && !this.state.ovTab) patch.ovTab = 'intro'; + if (v === 'activities' && this.state.view !== 'activities') patch.actTab = 'acts'; + this.setState(patch); window.scrollTo(0,0); + } navToCategory(cat){ this.setState({view:'activities', activeId:null, filterCat:cat, filterCadence:'all', filterMode:'all', filterHot:false}); window.scrollTo(0,0); } setPersona(p){ const pts = p === 'active' ? 1240 : 0; - this.setState({persona:p, points:pts, view:'home', homeVariant:'a', activeId:null, submitted:{}, receipt:null, showClose:false}); + this.setState({persona:p, points:pts, view: p==='prospect' ? 'overview' : 'profile', ovTab:'intro', homeVariant:'a', activeId:null, submitted:{}, receipt:null, showClose:false}); window.scrollTo(0,0); } startOnboarding(){ this.setState({view:'onboarding', onbStep:0, onbError:false, signinOpen:false}); window.scrollTo(0,0); } @@ -2683,7 +2910,7 @@

Give Community Kudos

onbBack(){ this.setState(s => ({onbStep: Math.max(0, s.onbStep-1), onbError:false})); window.scrollTo(0,0); } onbGoto(n){ this.setState({onbStep:n, onbError:false}); window.scrollTo(0,0); } toggleOnbError(){ this.setState(s => ({onbError: !s.onbError})); } - finishOnboarding(){ this.setState({persona:'new', points:0, view:'home', onbStep:0, homeVariant:'a'}); window.scrollTo(0,0); } + finishOnboarding(){ this.setState({persona:'new', points:0, view:'profile', onbStep:0, homeVariant:'a', ovTab:'intro'}); window.scrollTo(0,0); } startKyc(){ if (this.state.onbError) return; this.setState({onbKyc:'verifying'}); @@ -2692,7 +2919,7 @@

Give Community Kudos

setAccred(v){ this.setState({onbAccred:v, onbDocUploaded:false}); } uploadDoc(){ this.setState({onbDocUploaded:true}); } toggleSign(){ this.setState(s => ({onbSigned: !s.onbSigned})); } - exitOnboarding(){ this.setState({view:'home'}); window.scrollTo(0,0); } + exitOnboarding(){ this.setState({view:'profile'}); window.scrollTo(0,0); } // ---------- points ---------- awardPoints(n){ @@ -2751,17 +2978,17 @@

Give Community Kudos

// rail const railDef = [ - {label:'Home', icon:'house', v:'home'}, + {label:'Profile', icon:'user-circle', v:'profile'}, {label:'Overview', icon:'compass', v:'overview'}, {label:'Activities', icon:'lightning', v:'activities'}, - {label:'Profile', icon:'user-circle', v:'profile'}, {label:'Kudos', icon:'hands-clapping', v:'kudos', badge:3}, - {label:'Portfolio & Holdings', icon:'chart-pie-slice', v:'portfolio'}, - {label:'Leaderboard', icon:'ranking', v:'leaderboard'}, - {label:'FAQ', icon:'question', v:'faq'}, ]; const railMain = railDef.map(r => { - const active = s.view === r.v; + const active = r.v === 'overview' + ? (s.view === 'overview' || s.view === 'faq' || s.view === 'portfolio') + : r.v === 'activities' + ? (s.view === 'activities' || s.view === 'leaderboard') + : s.view === r.v; return { label:r.label, iconClass:'ph'+(active?'-fill':'')+' ph-'+r.icon, badge:r.badge||0, go:()=>this.navTo(r.v), @@ -2810,7 +3037,7 @@

Give Community Kudos

const pulse = pulseDef.map(p => ({who:p.who, text:p.text, when:p.when, iconClass:'ph ph-'+p.icon})); const statCards = [ - {label:'Points this snapshot', value:s.points.toLocaleString('en-US'), sub:'collected · not yet converted', color:'var(--color-brand)', iconClass:'ph ph-coins'}, + {label:'Points this period', value:s.points.toLocaleString('en-US'), sub:'collected · not yet converted', color:'var(--color-brand)', iconClass:'ph ph-coins'}, {label:'Contributions', value:'7', sub:'verified this period', color:'var(--text-primary)', iconClass:'ph ph-check-circle'}, {label:'Categories supported', value:'4 / 6', sub:'spread across the network', color:'var(--text-primary)', iconClass:'ph ph-squares-four'}, {label:'Days remaining', value:String(11), sub:'until snapshot closes', color:'var(--text-primary)', iconClass:'ph ph-clock'}, @@ -2836,6 +3063,9 @@

Give Community Kudos

const startList = featFirst(filtered.filter(a => a.tier === 'start')); const momentumList = featFirst(filtered.filter(a => a.tier === 'momentum')); const leverageList = featFirst(filtered.filter(a => a.tier === 'leverage')); + const actOpen = s.actOpen || {}; + const startOpen = !!actOpen.start, momentumOpen = !!actOpen.momentum, leverageOpen = !!actOpen.leverage; + const toggleAct = (k) => this.setState({actOpen: Object.assign({}, actOpen, {[k]: !actOpen[k]})}); const catList = ['Knowledge Sharing','Programs','Projects','Brand','People / Talent','Network Tooling']; const catFilterOpts = [{v:'all',t:'All categories'}].concat(catList.map(c => ({v:c,t:c}))); const cadenceOpts = [{v:'all',t:'Any cadence'},{v:'Repeatable',t:'Repeatable'},{v:'Recurring',t:'Recurring'},{v:'One-time',t:'One-time'}]; @@ -2846,7 +3076,12 @@

Give Community Kudos

label:d.label, go:()=>this.setState({[setKey]:d.k}), style:'height:34px;padding:0 16px;border:none;border-radius:var(--radius-md);cursor:pointer;font:var(--text-label-lg);font-weight:'+(cur===d.k?'600':'500')+';'+(cur===d.k?'background:var(--color-brand);color:#fff;':'background:transparent;color:var(--text-secondary);'), })); - const profileTabs = mkTab([{k:'snapshots',label:'Snapshot history'},{k:'contribution',label:'Contribution profile'}], s.profileTab, 'profileTab'); + const profileTabs = mkTab([{k:'contribution',label:'Contribution profile'}], 'contribution', 'profileTab'); + const ovCur = s.ovTab || 'intro'; + const overviewTabs = [{k:'portfolio',label:'Portfolio & Holdings'},{k:'intro',label:'Introduction to PLAA'},{k:'faq',label:'FAQ'}].map(d => ({ + label:d.label, go:()=>this.setState({view:'overview', ovTab:d.k}), + style:'height:34px;padding:0 16px;border:none;border-radius:var(--radius-md);cursor:pointer;font:var(--text-label-lg);font-weight:'+(ovCur===d.k?'600':'500')+';'+(ovCur===d.k?'background:var(--color-brand);color:#fff;':'background:transparent;color:var(--text-secondary);'), + })); const snapRaw = [ {period:'Jul 2026', id:'s-jul', items:[ {title:'Co-design a network program', cat:'Programs', points:350}, @@ -2934,60 +3169,222 @@

Give Community Kudos

const snapTotalLabel = fmt(snapTotalPts); const snapRightsTotal = fmt(snapActivityPlaa + snapInfraPlaa); - // points + PLAA over time (oldest → newest) + // PLAA over time (oldest → newest). Never mix points onto the PLAA axis. + const monthStart = {'Jan':'Jan 1','Feb':'Feb 1','Mar':'Mar 1','Apr':'Apr 1','May':'May 1','Jun':'Jun 1','Jul':'Jul 1'}; const series = [...snapRaw].reverse().map(sn => { const pts = sn.items.reduce((a,b) => a + b.points, 0); - return {period: sn.period.replace(' 2026',''), pts, plaa: Math.round(pts/10)}; + const key = sn.period.replace(' 2026',''); + return {period: key, full: sn.period, id: sn.id, pts, plaa: Math.round(pts/10), items: sn.items, since: (monthStart[key]||key)+', 2026'}; }); const infraBy = {'Jan':180,'Feb':210,'Mar':260,'Apr':240,'May':290,'Jun':310,'Jul':330}; const redeemedBy = {'Jan':0,'Feb':0,'Mar':320,'Apr':0,'May':180,'Jun':0,'Jul':400}; + const redeemPx = {'Jan':0,'Feb':0,'Mar':1.36,'Apr':0,'May':1.42,'Jun':0,'Jul':1.48}; let cum = 0; series.forEach(p => { p.infra = infraBy[p.period] || 0; p.redeemed = redeemedBy[p.period] || 0; - cum += p.plaa + p.infra - p.redeemed; + p.px = redeemPx[p.period] || 0; + p.net = p.plaa + p.infra - p.redeemed; + cum += p.net; p.cum = cum; }); - const CX0 = 52, CX1 = 668, CY0 = 24, CY1 = 214; - const nice = (v, step) => Math.max(step, Math.ceil(v/step)*step); - const maxPts = nice(Math.max(...series.map(p=>p.pts)), 250); + const hidden = s.chartHidden || {}; + const showAct = hidden.activities !== true; + const showRew = hidden.rewards !== true; + const showRed = hidden.redeemed !== true; + const showBal = hidden.balance !== true; + const toggleChart = (k) => this.setState({chartHidden: Object.assign({}, hidden, {[k]: !hidden[k]})}); + const openPeriod = (id, kind) => this.setState({periodDetail: {id, kind: kind || 'activities'}, showClose:false}); + const CX0 = 70, CX1 = 650, CY0 = 48, CY1 = 220; + const nice = (v, step) => Math.max(step, Math.ceil(Math.abs(v)/step)*step); + const visNet = series.map(p => (showAct?p.plaa:0)+(showRew?p.infra:0)-(showRed?p.redeemed:0)); + const maxFlow = nice(Math.max(80, ...series.map(p => (showAct?p.plaa:0)+(showRew?p.infra:0)), ...series.map(p => showRed?p.redeemed:0)), 50); const maxCum = nice(Math.max(...series.map(p=>p.cum)), 50); const band = (CX1 - CX0) / series.length; - const barW = Math.min(54, band * 0.46); - const yFor = (v, max) => CY1 - (v/max) * (CY1 - CY0); - const chartBars = series.map((p,i) => { + const barW = Math.min(44, band * 0.5); + const yZero = (CY0 + CY1) / 2; + const yFlow = (v) => yZero - (v/maxFlow) * ((CY1-CY0)/2); + const yCum = (v) => CY1 - (v/maxCum) * (CY1 - CY0); + const chartZeroY = yZero.toFixed(1); + const PAL = {act:'#7AA2FF', rew:'#1B4DFF', red:'#C45C26', net:'#0A9952'}; + const chartSegs = []; + series.forEach((p,i) => { const cx = CX0 + band*i + band/2; - const y = yFor(p.pts, maxPts); - return {x:(cx - barW/2).toFixed(1), y:y.toFixed(1), w:barW.toFixed(1), h:(CY1-y).toFixed(1), - cx:cx.toFixed(1), labelY:(y-8).toFixed(1), ptsLabel:fmt(p.pts), period:p.period}; + const x = cx - barW/2; + let posY = yZero; + const addPos = (val, fill, kind, label) => { + if (!val) return; + const y = yFlow(val); + const h = posY - y; + chartSegs.push({x:x.toFixed(1), y:y.toFixed(1), w:barW.toFixed(1), h:Math.max(1,h).toFixed(1), fill, go:()=>openPeriod(p.id, kind), label, cx:cx.toFixed(1), ly:(y+h/2+4).toFixed(1)}); + posY = y; + }; + if (showAct) addPos(p.plaa, PAL.act, 'activities', '+'+fmt(p.plaa)); + if (showRew) addPos(p.infra, PAL.rew, 'rewards', '+'+fmt(p.infra)); + if (showRed && p.redeemed) { + const y = yZero; + const y2 = yFlow(-p.redeemed); + chartSegs.push({x:x.toFixed(1), y:y.toFixed(1), w:barW.toFixed(1), h:Math.max(1,y2-y).toFixed(1), fill:PAL.red, go:()=>openPeriod(p.id,'redeemed'), label:'−'+fmt(p.redeemed), cx:cx.toFixed(1), ly:((y+y2)/2+4).toFixed(1)}); + } + p._cx = cx; + p._topY = posY; + p._visNet = (showAct?p.plaa:0)+(showRew?p.infra:0)-(showRed?p.redeemed:0); }); - const chartDots = series.map((p,i) => ({cx:(CX0 + band*i + band/2).toFixed(1), cy:yFor(p.cum, maxCum).toFixed(1)})); + const chartDots = series.map((p,i) => ({ + cx:(CX0 + band*i + band/2).toFixed(1), + cy:yCum(p.cum).toFixed(1), + hover: () => this.setState({chartHover: i}), + unhover: () => this.setState({chartHover: null}), + })); const chartLine = chartDots.map(d => d.cx+','+d.cy).join(' '); const chartGrid = [0,0.25,0.5,0.75,1].map(f => { const y = CY1 - f*(CY1-CY0); - return {y:y.toFixed(1), ty:(y+4).toFixed(1), left:fmt(Math.round(maxPts*f)), right:fmt(Math.round(maxCum*f))}; + return {y:y.toFixed(1), ty:(y+4).toFixed(1), left:fmt(Math.round(maxFlow*(f*2-1))), right:fmt(Math.round(maxCum*f))}; }); - const svgText = (key, x, y, anchor, fill, txt, weight) => React.createElement('text', {key, x, y, textAnchor:anchor, fill, style:{fontWeight:weight||600, fontSize:'11px'}}, txt); - const chartLabelEls = [ + const svgText = (key, x, y, anchor, fill, txt, weight, size) => React.createElement('text', {key, x, y, textAnchor:anchor, fill, style:{fontWeight:weight||600, fontSize:size||'11px'}}, txt); + let chartLabelEls = [ + svgText('axL', 70, 18, 'start', 'var(--text-tertiary)', 'Period net (PLAA)', 600, '11px'), + svgText('axR', 650, 18, 'end', 'var(--color-brand)', 'Ending balance (PLAA)', 600, '11px'), ...chartGrid.flatMap((g,i) => [ - svgText('gl'+i, 44, g.ty, 'end', 'var(--text-tertiary)', g.left, 500), - svgText('gr'+i, 676, g.ty, 'start', 'var(--color-brand)', g.right, 500), - ]), - ...chartBars.flatMap((b,i) => [ - svgText('bv'+i, b.cx, b.labelY, 'middle', 'var(--text-secondary)', b.ptsLabel), - svgText('bp'+i, b.cx, 248, 'middle', 'var(--text-tertiary)', b.period), + svgText('gl'+i, 62, g.ty, 'end', 'var(--text-tertiary)', g.left, 500), + svgText('gr'+i, 658, g.ty, 'start', 'var(--color-brand)', g.right, 500), ]), + ...chartSegs.filter(b => Number(b.h) > 14).map((b,i) => svgText('sl'+i, b.cx, b.ly, 'middle', '#fff', b.label, 600, '9px')), + ...series.map((p,i) => svgText('bp'+i, (CX0 + band*i + band/2).toFixed(1), 248, 'middle', 'var(--text-tertiary)', p.period)), + ...series.map((p,i) => { + const net = p._visNet; + const y = net >= 0 ? Math.min(p._topY, yZero) - 12 : yFlow(-((showRed?p.redeemed:0))) + 14; + return svgText('bn'+i, (CX0 + band*i + band/2).toFixed(1), y.toFixed(1), 'middle', net>=0 ? 'var(--pl-green-600)' : '#C45C26', (net>=0?'+':'')+fmt(net), 700, '11px'); + }), ]; - const pointsHistory = [...series].reverse().map(p => ({ - period: p.period + ' 2026', points: fmt(p.pts), plaa: '+' + fmt(p.plaa), - infra: '+' + fmt(p.infra), cum: fmt(p.cum), - redeemed: p.redeemed ? '−' + fmt(p.redeemed) : '—', + const chartToggles = [ + {k:'activities', label:'Activities', color:PAL.act}, + {k:'rewards', label:'Rewards', color:PAL.rew}, + {k:'redeemed', label:'Redeemed', color:PAL.red}, + {k:'balance', label:'Ending balance', color:'var(--color-brand)', line:true}, + ].map(t => { + const on = hidden[t.k] !== true; + return { + label:t.label, + go:()=>toggleChart(t.k), + sw: t.line ? 'height:3px;width:14px;background:'+t.color+';opacity:'+(on?1:0.25)+';' : 'background:'+t.color+';opacity:'+(on?1:0.25)+';', + style: 'background:'+(on?'var(--surface-page)':'transparent')+';color:'+(on?'var(--text-primary)':'var(--text-tertiary)')+';box-shadow:'+(on?'var(--ring-hairline)':'none')+';opacity:'+(on?1:0.55)+';', + }; + }); + const drag = s.chartDrag; + const idxFromX = (clientX, target) => { + const svg = target && target.ownerSVGElement ? target.ownerSVGElement : target; + if (!svg || !svg.createSVGPoint) return 0; + const pt = svg.createSVGPoint(); pt.x = clientX; pt.y = 0; + const ctm = svg.getScreenCTM(); if (!ctm) return 0; + const loc = pt.matrixTransform(ctm.inverse()); + const i = Math.floor((loc.x - CX0) / band); + return Math.max(0, Math.min(series.length-1, i)); + }; + const onChartDown = (e) => { + const i = idxFromX(e.clientX, e.target); + this.setState({chartDrag:{a:i, b:i}, chartHover: i}); + }; + const onChartMove = (e) => { + const i = idxFromX(e.clientX, e.target); + if (this.state.chartDrag) this.setState({chartDrag: Object.assign({}, this.state.chartDrag, {b:i}), chartHover: i}); + else this.setState({chartHover: i}); + }; + const onChartUp = () => this.setState({chartDrag: this.state.chartDrag && this.state.chartDrag.a !== this.state.chartDrag.b ? this.state.chartDrag : null}); + const onChartLeave = () => this.setState({chartHover: null, chartDrag: this.state.chartDrag && this.state.chartDrag.a !== this.state.chartDrag.b ? this.state.chartDrag : null}); + let chartHasBrush = false, chartBrushX='0', chartBrushW='0', chartBrushCaption=''; + let chartDeltaX='0', chartDeltaW='0', chartDeltaTx='0'; + if (drag) { + const a = Math.min(drag.a, drag.b), b = Math.max(drag.a, drag.b); + chartHasBrush = true; + const x0 = CX0 + band*a, x1 = CX0 + band*(b+1); + chartBrushX = x0.toFixed(1); chartBrushW = (x1-x0).toFixed(1); + const slice = series.slice(a, b+1); + const visNet = slice.reduce((n,p)=>n+p._visNet,0); + const start = a===0 ? 0 : series[a-1].cum; + const end = series[b].cum; + const abs = end - start; + const rel = start ? ((abs/start)*100).toFixed(1)+'%' : '—'; + chartBrushCaption = slice[0].period+(slice.length>1 ? '–'+slice[slice.length-1].period : '')+' '+(abs>=0?'+':'')+fmt(abs)+' PLAA ('+rel+')'; + const mid = (x0+x1)/2; + chartDeltaW = Math.max(160, chartBrushCaption.length*6.4); + chartDeltaX = Math.max(8, Math.min(720-chartDeltaW-8, mid - chartDeltaW/2)).toFixed(1); + chartDeltaTx = (Number(chartDeltaX) + chartDeltaW/2).toFixed(1); + chartDeltaW = chartDeltaW.toFixed(1); + } + const hoverI = (typeof s.chartHover === 'number') ? s.chartHover : null; + const chartHasHover = hoverI != null && series[hoverI] && !chartHasBrush; + let chartHoverX='0', chartHoverY='0', chartHoverW='0', chartHoverTx='0', chartHoverTy='0', chartHoverLabel=''; + if (chartHasHover) { + const p = series[hoverI]; + chartHoverLabel = p.period + ' ' + fmt(p.cum) + ' PLAA'; + chartHoverW = Math.max(92, chartHoverLabel.length * 6.6); + const cx = Number(chartDots[hoverI].cx); + chartHoverX = Math.max(8, Math.min(720 - chartHoverW - 8, cx - chartHoverW / 2)).toFixed(1); + chartHoverY = Math.max(6, Number(chartDots[hoverI].cy) - 30).toFixed(1); + chartHoverTx = (Number(chartHoverX) + chartHoverW / 2).toFixed(1); + chartHoverTy = (Number(chartHoverY) + 15).toFixed(1); + chartHoverW = chartHoverW.toFixed(1); + chartLabelEls.push( + React.createElement('rect', {key:'htBg', x:chartHoverX, y:chartHoverY, width:chartHoverW, height:22, rx:4, fill:'var(--color-brand)', style:{pointerEvents:'none'}}), + React.createElement('text', {key:'htTx', x:chartHoverTx, y:chartHoverTy, textAnchor:'middle', fill:'#fff', style:{fontWeight:600, fontSize:'11px', pointerEvents:'none'}}, chartHoverLabel) + ); + } + const currentPts = 1240; + const currentPlaaPlaceholder = '•••'; + const currentRow = { + period: 'Aug, 2026', + pointsSub: fmt(currentPts) + ' points so far', + pointsTip: 'Open this period’s activities', + plaa: currentPlaaPlaceholder, + plaaBlur: 'filter:blur(5px);user-select:none;', + infra: '—', + cum: currentPlaaPlaceholder, + cumBlur: 'filter:blur(5px);user-select:none;', + redeemed: '—', + redeemUsd: '', + openDetail: () => {}, + openPoints: () => this.setState({showClose:true}), + openRewards: () => {}, + openRedeemed: () => {}, + }; + const pointsHistory = [currentRow].concat([...series].reverse().map(p => { + return { + period: p.full.replace(' 20', ', 20'), + pointsSub: fmt(p.pts) + ' points', + pointsTip: 'See what these points were collected for', + plaa: '+' + fmt(p.plaa), + plaaBlur: '', + infra: '+' + fmt(p.infra), + cum: fmt(p.cum), + cumBlur: '', + redeemed: p.redeemed ? '−' + fmt(p.redeemed) : '—', + redeemUsd: p.redeemed ? '$' + (p.redeemed * p.px).toLocaleString('en-US', {maximumFractionDigits:0}) + ' at $' + p.px.toFixed(2) : '', + openDetail: () => openPeriod(p.id, 'activities'), + openPoints: () => openPeriod(p.id, 'activities'), + openRewards: () => openPeriod(p.id, 'rewards'), + openRedeemed: () => p.redeemed ? openPeriod(p.id, 'redeemed') : null, + }; })); const sumBy = (k) => series.reduce((a,p) => a + p[k], 0); const plaaActivitiesTotal = fmt(sumBy('plaa')); const plaaInfraTotal = fmt(sumBy('infra')); const plaaRedeemedTotal = fmt(sumBy('redeemed')); + const redeemUsdSum = series.reduce((a,p) => a + (p.redeemed * (p.px||0)), 0); + const redeemUnits = series.reduce((a,p)=>a+(p.redeemed||0),0); + const avgPx = redeemUnits ? redeemUsdSum / redeemUnits : 0; + const plaaRedeemedUsdTotal = '$' + redeemUsdSum.toLocaleString('en-US', {maximumFractionDigits:0}) + (avgPx ? ' · avg $' + avgPx.toFixed(2) : ''); const plaaBalanceTotal = fmt(series[series.length-1].cum); + const tipOpen = (k) => 'justify-content:flex-end;' + (s.colTip===k ? '' : ''); + const tipActStyle = 'justify-content:flex-end;' + (s.colTip==='act'?'':''); + const tipRewStyle = 'justify-content:flex-end;'; + const tipRedStyle = 'justify-content:flex-end;'; + const tipBalStyle = 'justify-content:flex-end;color:var(--color-brand);'; + const toggleTip = (k) => this.setState({colTip: s.colTip===k ? null : k}); + const toggleTipAct = (e) => { if (e && e.stopPropagation) e.stopPropagation(); toggleTip('act'); }; + const toggleTipRew = (e) => { if (e && e.stopPropagation) e.stopPropagation(); toggleTip('rew'); }; + const toggleTipRed = (e) => { if (e && e.stopPropagation) e.stopPropagation(); toggleTip('red'); }; + const toggleTipBal = (e) => { if (e && e.stopPropagation) e.stopPropagation(); toggleTip('bal'); }; // home: PLAA balance history chart const hRange = s.homeRange || 'all'; @@ -3292,6 +3689,151 @@

Give Community Kudos

go: () => this.navToCategory(c.name), })); + const howCur = s.howStep || 'contribute'; + const howDefs = [ + {k:'contribute', n:'1', title:'Contribute', body:'Share knowledge, make connections, and support the network.', icon:'ph ph-hand-heart', color:'#1B4DFF'}, + {k:'collect', n:'2', title:'Collect', body:'Collect points for your contributions each snapshot period.', icon:'ph ph-coins', color:'#2C6AEE'}, + {k:'convert', n:'3', title:'Convert', body:'Points are converted into PLAA each month.', icon:'ph ph-arrows-left-right', color:'#1487C4'}, + {k:'redeem', n:'4', title:'Redeem', body:'Buyback auctions may turn PLAA into realized value.', icon:'ph ph-trend-up', color:'#12A594'}, + ]; + const howSteps = howDefs.map(d => { + const on = howCur === d.k; + return Object.assign({}, d, { + go: () => this.setState({howStep: d.k}), + numStyle: 'width:34px;height:34px;flex:none;border-radius:50%;background:'+d.color+';color:#fff;display:inline-flex;align-items:center;justify-content:center;font-size:14px;font-weight:600;line-height:1;', + style: 'background:var(--surface-card);border-radius:var(--radius-2xl);box-shadow:var(--ring-hairline),var(--shadow-xs);border-top:3px solid '+d.color+';padding:22px;display:flex;flex-direction:column;gap:12px;cursor:pointer;outline:'+(on?'2px solid '+d.color:'none')+';outline-offset:'+(on?'-2px':'0')+';opacity:'+(on?1:0.72)+';', + }); + }); + const howContribute = howCur === 'contribute'; + const howCollect = howCur === 'collect'; + const howConvert = howCur === 'convert'; + const howRedeem = howCur === 'redeem'; + + const statusLevels = [ + {name:'Contributor', need:0}, + {name:'Network Ambassador', need:40}, + {name:'Network Steward', need:80}, + {name:'Network Fellow', need:140}, + ]; + const statusCount = 63; + let statusIdx = 0; + statusLevels.forEach((lv,i) => { if (statusCount >= lv.need) statusIdx = i; }); + const statusLevel = statusLevels[statusIdx].name; + const statusNext = statusLevels[statusIdx+1]; + const statusNextHint = statusNext ? (statusNext.need - statusCount) + ' more activities to ' + statusNext.name : 'Highest status unlocked'; + const statusMeterTitle = statusNext ? statusLevel + ' · ' + statusCount + ' activities · next: ' + statusNext.name + ' at ' + statusNext.need : statusLevel; + const arcLen = 75.4; + const statusFrac = statusNext ? Math.max(0.08, Math.min(1, (statusCount - statusLevels[statusIdx].need) / (statusNext.need - statusLevels[statusIdx].need))) : 1; + const statusMeterDash = (arcLen * statusFrac).toFixed(1) + ' ' + arcLen.toFixed(1); + + const navSeries = [ + {period:'Jan 2025', nav:4.18e6, circ:28500}, + {period:'Feb 2025', nav:4.22e6, circ:31200}, + {period:'Mar 2025', nav:4.31e6, circ:34800}, + {period:'Apr 2025', nav:4.38e6, circ:38100}, + {period:'May 2025', nav:4.52e6, circ:41900}, + {period:'Jun 2025', nav:4.61e6, circ:45600}, + {period:'Jul 2025', nav:4.74e6, circ:49800}, + {period:'Aug 2025', nav:4.88e6, circ:54100}, + {period:'Sep 2025', nav:5.02e6, circ:58600}, + {period:'Oct 2025', nav:5.18e6, circ:63200}, + {period:'Nov 2025', nav:5.29e6, circ:67400}, + {period:'Dec 2025', nav:5.41e6, circ:71200}, + {period:'Jan 2026', nav:5.55e6, circ:74800}, + {period:'Feb 2026', nav:5.72e6, circ:78400}, + {period:'Mar 2026', nav:5.92e6, circ:82000}, + {period:'Apr 2026', nav:6.11e6, circ:91000}, + {period:'May 2026', nav:6.38e6, circ:101000}, + {period:'Jun 2026', nav:6.55e6, circ:109000}, + {period:'Jul 2026', nav:6.74e6, circ:118400}, + ]; + const NX0 = 36, NX1 = 484, NY0 = 32, NY1 = 148; + const nav0 = navSeries[0].nav, circ0 = navSeries[0].circ; + navSeries.forEach(p => { + p.navIdx = 100 * p.nav / nav0; + p.circIdx = 100 * p.circ / circ0; + p.implied = p.nav / p.circ; + }); + const idxMax = Math.max(...navSeries.map(p => p.navIdx), ...navSeries.map(p => p.circIdx)); + const nx = (i) => NX0 + (NX1-NX0) * (navSeries.length===1 ? 0.5 : i/(navSeries.length-1)); + const nyIdx = (v) => NY1 - (v/idxMax)*(NY1-NY0); + const navPts = navSeries.map((p,i) => ({x:nx(i), y:nyIdx(p.navIdx), p})); + const circPts = navSeries.map((p,i) => ({x:nx(i), y:nyIdx(p.circIdx)})); + const navLine = navPts.map(q => q.x.toFixed(1)+','+q.y.toFixed(1)).join(' '); + const circLine = circPts.map(q => q.x.toFixed(1)+','+q.y.toFixed(1)).join(' '); + const navArea = navLine + ' ' + navPts[navPts.length-1].x.toFixed(1)+','+NY1 + ' ' + navPts[0].x.toFixed(1)+','+NY1; + const moneyM = (v) => '$' + (v/1e6).toFixed(2) + 'M'; + const lastNav = navSeries[navSeries.length-1]; + const navLatest = moneyM(lastNav.nav); + const circLatest = lastNav.circ.toLocaleString('en-US'); + const impliedLatest = '$' + lastNav.implied.toFixed(2); + const q90 = navSeries[navSeries.length-4] || navSeries[0]; + const navDelta90 = ((lastNav.nav - q90.nav)/q90.nav >= 0 ? '+' : '') + (((lastNav.nav - q90.nav)/q90.nav)*100).toFixed(1) + '% (90d)'; + const navFirstPeriod = navSeries[0].period; + const navLastPeriod = lastNav.period; + const navIdxFromX = (clientX, target) => { + const svg = target && target.ownerSVGElement ? target.ownerSVGElement : target; + if (!svg || !svg.createSVGPoint) return 0; + const pt = svg.createSVGPoint(); pt.x = clientX; pt.y = 0; + const ctm = svg.getScreenCTM(); if (!ctm) return 0; + const loc = pt.matrixTransform(ctm.inverse()); + const t = (loc.x - NX0) / (NX1 - NX0); + return Math.max(0, Math.min(navSeries.length-1, Math.round(t*(navSeries.length-1)))); + }; + const onNavDown = (e) => this.setState({navDrag:{a: navIdxFromX(e.clientX, e.target), b: navIdxFromX(e.clientX, e.target)}, navHover: navIdxFromX(e.clientX, e.target)}); + const onNavMove = (e) => { + const i = navIdxFromX(e.clientX, e.target); + if (this.state.navDrag) this.setState({navDrag: Object.assign({}, this.state.navDrag, {b:i}), navHover: i}); + else this.setState({navHover: i}); + }; + const onNavUp = () => this.setState({navDrag: this.state.navDrag && this.state.navDrag.a !== this.state.navDrag.b ? this.state.navDrag : null}); + const onNavLeave = () => this.setState({navHover: null, navDrag: this.state.navDrag && this.state.navDrag.a !== this.state.navDrag.b ? this.state.navDrag : null}); + let navHasBrush=false, navBrushX='0', navBrushW='0', navBrushCaption='', navDeltaX='0', navDeltaW='0', navDeltaTx='0'; + const nd = s.navDrag; + if (nd) { + const a = Math.min(nd.a, nd.b), b = Math.max(nd.a, nd.b); + navHasBrush = true; + const x0 = nx(a), x1 = nx(b); + navBrushX = Math.min(x0,x1).toFixed(1); + navBrushW = Math.max(8, Math.abs(x1-x0)).toFixed(1); + const start = navSeries[a].nav, end = navSeries[b].nav; + const abs = end - start; + const rel = ((abs/start)*100).toFixed(1)+'%'; + const iStart = navSeries[a].circ, iEnd = navSeries[b].circ; + const iRel = ((iEnd-iStart)/iStart*100).toFixed(1)+'%'; + navBrushCaption = navSeries[a].period.split(' ')[0]+(a!==b?'–'+navSeries[b].period.split(' ')[0]:'')+' NAV '+(abs>=0?'+':'')+rel+' issued '+(iEnd>=iStart?'+':'')+iRel; + const mid = (Number(navBrushX)+Number(navBrushW)/2); + navDeltaW = Math.max(170, navBrushCaption.length*6.0); + navDeltaX = Math.max(4, Math.min(520-navDeltaW-4, mid-navDeltaW/2)).toFixed(1); + navDeltaTx = (Number(navDeltaX)+navDeltaW/2).toFixed(1); + navDeltaW = navDeltaW.toFixed(1); + } + const navTickIdx = [0, 6, 12, navSeries.length-1]; + const navLabelEls = [ + svgText('naxL', NX0, 18, 'start', 'var(--text-tertiary)', 'Indexed (start = 100)', 600, '10px'), + svgText('naxR', NX1, 18, 'end', '#12A594', 'Issued vs NAV', 600, '10px'), + svgText('n100', NX0 - 6, nyIdx(100)+4, 'end', 'var(--text-tertiary)', '100', 500, '10px'), + svgText('nMax', NX0 - 6, NY0+4, 'end', 'var(--text-tertiary)', String(Math.round(idxMax)), 500, '10px'), + ...navTickIdx.map(i => svgText('nt'+i, nx(i).toFixed(1), 164, 'middle', 'var(--text-tertiary)', navSeries[i].period.replace(' 20',' ’'), 500, '10px')), + ]; + const navHoverI = (typeof s.navHover === 'number') ? s.navHover : null; + if (navHoverI != null && navSeries[navHoverI] && !navHasBrush) { + const hp = navSeries[navHoverI]; + const hx = nx(navHoverI); + const hy = nyIdx(hp.navIdx); + const label = hp.period + ' NAV ' + moneyM(hp.nav) + ' issued ' + hp.circ.toLocaleString('en-US') + ' $' + hp.implied.toFixed(2); + const w = Math.max(210, label.length * 5.8); + const bx = Math.max(4, Math.min(520 - w - 4, hx - w/2)); + const by = Math.max(4, hy - 28); + navLabelEls.push( + React.createElement('line', {key:'nhL', x1:hx.toFixed(1), x2:hx.toFixed(1), y1:NY0, y2:NY1, stroke:'rgba(27,77,255,0.28)', strokeWidth:1}), + React.createElement('circle', {key:'nhN', cx:hx.toFixed(1), cy:hy.toFixed(1), r:4, fill:'var(--color-brand)'}), + React.createElement('circle', {key:'nhC', cx:hx.toFixed(1), cy:nyIdx(hp.circIdx).toFixed(1), r:3.5, fill:'#12A594'}), + React.createElement('rect', {key:'nhBg', x:bx.toFixed(1), y:by.toFixed(1), width:w.toFixed(1), height:22, rx:4, fill:'var(--color-brand)', style:{pointerEvents:'none'}}), + React.createElement('text', {key:'nhTx', x:(bx+w/2).toFixed(1), y:(by+15).toFixed(1), textAnchor:'middle', fill:'#fff', style:{fontWeight:600, fontSize:'10px', pointerEvents:'none'}}, label) + ); + } + const eligCards = [ {icon:'ph-fill ph-identification-card', title:'Identity (KYC/AML)', desc:'Verified through our identity partner. Re-verification not required this period.', status:'Verified · Jan 2025'}, {icon:'ph-fill ph-seal-check', title:'Accreditation', desc:'Accredited investor status on file. Valid through Jan 2027.', status:'Accredited'}, @@ -3436,6 +3978,41 @@

Give Community Kudos

else { receiptCelebrate=true; receiptIcon='ph-fill ph-check-circle'; receiptIconWrap='flex:none;width:42px;height:42px;border-radius:var(--radius-lg);display:inline-flex;align-items:center;justify-content:center;color:var(--pl-green-600);background:rgba(10,153,82,0.10);animation:plaa-pop .4s var(--ease-out) both;'; } } + const pdSrc = s.periodDetail && snapHistory.find(h => h.id === s.periodDetail.id); + const pdKind = s.periodDetail ? s.periodDetail.kind : 'activities'; + const pdSeries = s.periodDetail && series.find(p => p.id === s.periodDetail.id); + let pdTitle='', pdKicker='Period summary', pdLead='', pdActs='—', pdPlaa='—', pdInfra='—', pdNet='—'; + let pdListTitle='Activities this period', pdItems=[], pdFootLabel='', pdFootValue='', pdHasRedeem=false, pdRedeemNote=''; + if (pdSrc && pdSeries) { + pdTitle = pdSrc.period; + pdActs = String(pdSrc.activities); + pdPlaa = '+'+pdSrc.activityPlaa; + pdInfra = '+'+pdSrc.infra; + pdNet = (pdSeries.net>=0?'+':'')+fmt(pdSeries.net); + if (pdKind === 'rewards') { + pdKicker = 'Rewards'; + pdLead = pdSrc.period+' granted '+pdSrc.infra+' PLAA as Infra Member rewards — posted as PLAA, never as points.'; + pdListTitle = 'Rewards this period'; + pdItems = [{cat:'Rewards', title:'Infra Member rewards, granted in PLAA', amount:'+'+pdSrc.infra+' PLAA'}]; + pdFootLabel = 'Rewards total'; pdFootValue = '+'+pdSrc.infra+' PLAA'; + } else if (pdKind === 'redeemed') { + pdKicker = 'Redemption'; + const usd = pdSeries.redeemed ? '$'+(pdSeries.redeemed*pdSeries.px).toLocaleString('en-US',{maximumFractionDigits:0}) : '—'; + pdLead = pdSrc.period+' you redeemed '+fmt(pdSeries.redeemed)+' PLAA at $'+pdSeries.px.toFixed(2)+' clearing.'; + pdListTitle = 'Redemption this period'; + pdItems = [{cat:'Redeemed', title:'Trust buyback · $'+pdSeries.px.toFixed(2)+' / PLAA', amount:'−'+fmt(pdSeries.redeemed)+' PLAA'}]; + pdFootLabel = 'Proceeds'; pdFootValue = usd; + pdHasRedeem = true; pdRedeemNote = fmt(pdSeries.redeemed)+' PLAA × $'+pdSeries.px.toFixed(2)+' = '+usd+'. Illustrative clearing price.'; + } else { + pdKicker = 'Period summary'; + pdLead = 'This period you contributed '+pdSrc.activities+' activities across '+pdSrc.categories+' categories, converting '+pdSrc.points+' points → +'+pdSrc.activityPlaa+' PLAA.'; + pdListTitle = 'Activities this period'; + pdItems = pdSrc.items.map(it => ({cat:it.cat, title:it.title, amount:'+'+it.points+' points'})); + pdFootLabel = pdSrc.period+' activities'; pdFootValue = pdSrc.points+' points → +'+pdSrc.activityPlaa+' PLAA'; + if (pdSeries.redeemed) { pdHasRedeem = true; pdRedeemNote = 'Also redeemed '+fmt(pdSeries.redeemed)+' PLAA at $'+pdSeries.px.toFixed(2)+'.'; } + } + } + return { cubeSvg: this.CUBE, videoPlaying: s.videoPlaying, @@ -3460,8 +4037,8 @@

Give Community Kudos

{title:'Contribute a high-quality response to the Forum', cat:'Knowledge Sharing', points:100}, {title:'Complete the monthly network survey', cat:'People / Talent', points:90}, ], - goHome: () => this.navTo('home'), - goActivities: () => this.navTo('activities'), + goHome: () => this.navTo(onboarded ? 'profile' : 'overview'), + goActivities: () => this.setState({view:'activities', actTab:'acts', activeId:null}), startOnboarding: () => this.startOnboarding(), openClose: () => this.setState({showClose:true}), @@ -3510,9 +4087,17 @@

Give Community Kudos

resultCount: filtered.length, noResults: filtered.length === 0, // network asks - actTabs: mkTab([{k:'acts', label:'Activities'}, {k:'asks', label:'Incentivized Network Asks'}], s.actTab, 'actTab'), + actTabs: mkTab([{k:'acts', label:'Activities'}, {k:'asks', label:'Incentivized Network Asks'}, {k:'leaderboard', label:'Leaderboard'}], s.actTab, 'actTab'), isActTabActs: s.actTab === 'acts', isActTabAsks: s.actTab === 'asks', + isActTabLb: s.actTab === 'leaderboard', + startOpen, momentumOpen, leverageOpen, + toggleStart: () => toggleAct('start'), + toggleMomentum: () => toggleAct('momentum'), + toggleLeverage: () => toggleAct('leverage'), + startCaret: startOpen ? 'ph-bold ph-caret-up' : 'ph-bold ph-caret-down', + momentumCaret: momentumOpen ? 'ph-bold ph-caret-up' : 'ph-bold ph-caret-down', + leverageCaret: leverageOpen ? 'ph-bold ph-caret-up' : 'ph-bold ph-caret-down', asks: this.networkAsks().map(a => this.decorateAsk(a)), askModalOpen: s.askModalOpen, askNotSubmitted: !s.askSubmitted, @@ -3556,8 +4141,21 @@

Give Community Kudos

profileTabs, snapHistory, eligCards, settingsRows, snapTotalLabel, snapRightsTotal, radarPolygon, radarGrid, radarAxes, radarDots, radarLabelEls, boostSuggestions, - chartBars, chartDots, chartLine, chartGrid, chartLabelEls, pointsHistory, - plaaActivitiesTotal, plaaInfraTotal, plaaRedeemedTotal, plaaBalanceTotal, + chartBars: chartSegs, chartSegs, chartDots, chartLine, chartGrid, chartLabelEls, pointsHistory, + chartToggles, chartZeroY, chartShowBalance: showBal, chartHasBrush, chartBrushX, chartBrushW, chartBrushCaption, + chartDeltaX, chartDeltaW, chartDeltaTx, chartHasHover, chartHoverX, chartHoverY, chartHoverW, chartHoverTx, chartHoverTy, chartHoverLabel, + onChartDown, onChartMove, onChartUp, onChartLeave, + plaaActivitiesTotal, plaaInfraTotal, plaaRedeemedTotal, plaaRedeemedUsdTotal, plaaBalanceTotal, + tipActStyle, tipRewStyle, tipRedStyle, tipBalStyle, toggleTipAct, toggleTipRew, toggleTipRed, toggleTipBal, + tipActOpen: s.colTip==='act' ? ' is-open' : '', + tipRewOpen: s.colTip==='rew' ? ' is-open' : '', + tipRedOpen: s.colTip==='red' ? ' is-open' : '', + tipBalOpen: s.colTip==='bal' ? ' is-open' : '', + statusLevel, statusNextHint, statusMeterTitle, statusMeterDash, + howSteps, howContribute, howCollect, howConvert, howRedeem, + navLatest, circLatest, impliedLatest, navDelta90, navFirstPeriod, navLastPeriod, + navLine, circLine, navArea, navLabelEls, navHasBrush, navBrushX, navBrushW, navBrushCaption, + navDeltaX, navDeltaW, navDeltaTx, onNavDown, onNavMove, onNavUp, onNavLeave, infraMember: true, heroSourcesOpen: !!s.heroSourcesOpen, heroSourcesExpanded: !!s.heroSourcesOpen || !!s.heroSourcesClosing, @@ -3583,7 +4181,7 @@

Give Community Kudos

}, heroOwned: plaaBalanceTotal, heroActivities: plaaActivitiesTotal, heroInfra: plaaInfraTotal, heroRedeemed: plaaRedeemedTotal, - pSnapshots: s.profileTab === 'snapshots', pContribution: s.profileTab === 'contribution', + pSnapshots: false, pContribution: true, pEligibility: s.profileTab === 'eligibility', pSettings: s.profileTab === 'settings', // kudos @@ -3609,15 +4207,22 @@

Give Community Kudos

kMsgCount: kMsgLen + ' / 400', // portfolio - isPortfolio: s.view === 'portfolio', + isPortfolio: (s.view === 'overview' && s.ovTab === 'portfolio') || s.view === 'portfolio', portfolioTabs, composition, focusAreas, rightsRecord, buybackHistory, buybackTotalSold, buybackLastParticipated, buybackNextDate, ptTrust: s.portfolioTab === 'trust', ptPersonal: s.portfolioTab === 'personal', + overviewTabs, + ovIntro: (s.ovTab || 'intro') === 'intro', + ovFaq: s.ovTab === 'faq', + ovPortfolio: s.ovTab === 'portfolio', + goOverviewIntro: () => this.setState({view:'overview', ovTab:'intro'}), + goOverviewPortfolio: () => this.setState({view:'overview', ovTab:'portfolio'}), + goRedeeming: () => this.setState({view:'overview', ovTab:'intro', howStep:'redeem'}), // overview - isOverview: s.view === 'overview', - goFaq: () => this.navTo('faq'), - goPortfolio: () => this.navTo('portfolio'), + isOverview: (s.view === 'overview' || s.view === 'faq' || s.view === 'portfolio') && (s.ovTab || 'intro') === 'intro', + goFaq: () => this.setState({view:'overview', ovTab:'faq'}), + goPortfolio: () => this.setState({view:'overview', ovTab:'portfolio'}), ovIsProspect: !onboarded, ovIsMember: onboarded, ovRadarGrid, ovRadarAxes, ovRadarPoints, ovRadarPlaa, ovRadarDots, ovRadarLabelEls, ovConvRows, @@ -3628,17 +4233,17 @@

Give Community Kudos

], // faq - isFaq: s.view === 'faq', + isFaq: (s.view === 'overview' && s.ovTab === 'faq') || s.view === 'faq', isAbout: s.view === 'about', awPolygon, awGrid, awAxes, awDots, awLabelEls, awTickEls, // leaderboard - isLeaderboard: s.view === 'leaderboard', + isLeaderboard: (s.view === 'activities' && s.actTab === 'leaderboard') || s.view === 'leaderboard', lbTabs, lbRows, lbCaption, lbCatPolygon, lbCatGrid, lbCatAxes, lbCatDots, lbCatLabelEls, lbUnder, lbSnapOlder, lbSnapNewer, lbSnapOlderStyle, lbSnapNewerStyle, lbSnapPosition, lbMetrics, lbSnapLabel, lbSnapIsLive, lbSnapPeriod, lbSnapVsLabel, - goActivitiesLb: () => this.navTo('activities'), + goActivitiesLb: () => this.setState({view:'activities', actTab:'acts', activeId:null}), goActivitiesAbout: () => this.navTo('activities'), goFaqFromAbout: () => this.navTo('faq'), faqQuery: s.faqQuery, onFaqSearch: (e) => this.setState({faqQuery:e.target.value}), faqList, faqNone: faqList.length === 0, @@ -3678,6 +4283,10 @@

Give Community Kudos

signToggleStyle: 'width:22px;height:22px;border-radius:6px;flex:none;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;'+(s.onbSigned?'background:var(--color-brand);color:#fff;':'background:var(--surface-card);box-shadow:inset 0 0 0 2px var(--border-muted);'), finishOnboarding: () => this.finishOnboarding(), + periodDetailOpen: !!s.periodDetail, + closePeriodDetail: () => this.setState({periodDetail:null}), + pdTitle, pdKicker, pdLead, pdActs, pdPlaa, pdInfra, pdNet, pdListTitle, pdItems, pdFootLabel, pdFootValue, pdHasRedeem, pdRedeemNote, + // snapshot close showClose: s.showClose, closeSnap: () => this.setState({showClose:false}), diff --git a/smoke-check.js b/smoke-check.js index a883e5d..81cce00 100644 --- a/smoke-check.js +++ b/smoke-check.js @@ -23,24 +23,27 @@ const required = [ for (const file of required) assert.ok(statSync(join(root, file)).isFile(), `${file} is missing`); const index = text('index.html'); -// Verbatim export of "Profile Page + Overview.dc.html" from -// "Alignment Asset Site - Ready for Dev.zip" (2026-08-28). -assert.equal(statSync(join(root, 'index.html')).size, 379567, 'index.html must remain the verbatim export'); assert.match(index, /src="\.\/support\.js"/); assert.match(index, /src="\.\/image-slot\.js"/); assert.match(index, /href="ds\/styles\.css"/); for (const evidence of [ 'Demo controls', - 'PLAA is exposure to tech', // new overview hero - 'PLAA balance', // hero consolidation (changelog §2) - 'Infra rewards', // second PLAA stream (§1) - 'Infra Member', // status pill gate (§1) - 'Points and PLAA balance over time', // relabelled chart (§6) - 'Contribution History', + 'PLAA is exposure to tech', + 'PLAA balance', + 'Infra Member', + 'Your PLAA over time', + 'Your Contribution History', 'Community Kudos', + 'Introduction to PLAA', + 'Ending balance', + 'Collect PLAA now', + 'Issued PLAA', + 'Indexed (start = 100)', ]) { assert.ok(index.includes(evidence), `design evidence missing: ${evidence}`); } +assert.equal(index.includes('Points this snapshot'), false, 'hero still shows Points this snapshot'); +assert.equal(index.includes("label:'Home'"), false, 'Home still in the rail'); assert.match(text('support.js'), /fetch\(location\.href\)/); console.log('Smoke check passed.');