@subvalue/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/PRE_RELEASE.md +51 -0
  3. package/PRIVACY.md +45 -0
  4. package/README.md +119 -0
  5. package/dist/PRICING.md +46 -0
  6. package/dist/cli.js +2 -0
  7. package/dist/packages/cli/src/index.js +24 -0
  8. package/dist/packages/cli/src/runtime.js +22 -0
  9. package/dist/packages/cli/src/server.js +75 -0
  10. package/dist/packages/core/src/demo.js +10 -0
  11. package/dist/packages/core/src/metadata.js +14 -0
  12. package/dist/packages/core/src/pricing/current.js +326 -0
  13. package/dist/packages/core/src/pricing/index.js +84 -0
  14. package/dist/packages/core/src/providers/claude/index.js +24 -0
  15. package/dist/packages/core/src/providers/codex/index.js +51 -0
  16. package/dist/packages/core/src/scanner.js +48 -0
  17. package/dist/packages/core/src/security.js +41 -0
  18. package/dist/packages/core/src/storage.js +63 -0
  19. package/dist/packages/core/src/subscriptions.js +22 -0
  20. package/dist/packages/core/src/summary.js +55 -0
  21. package/dist/packages/core/src/types.js +29 -0
  22. package/dist/packages/receipt/src/index.js +90 -0
  23. package/dist/packages/ui/src/brand.js +5 -0
  24. package/dist/public/app.css +65 -0
  25. package/dist/public/app.js +1 -0
  26. package/dist/public/apps/web/src/landing.js +10 -0
  27. package/dist/public/apps/web/src/main.js +47 -0
  28. package/dist/public/brands/SOURCES.txt +14 -0
  29. package/dist/public/brands/anthropic-symbol-ivory.svg +4 -0
  30. package/dist/public/brands/claude-code-clawd.svg +3 -0
  31. package/dist/public/brands/claude-code-ivory.svg +28 -0
  32. package/dist/public/brands/codex-outline.svg +5 -0
  33. package/dist/public/brands/openai-blossom-white.svg +15 -0
  34. package/dist/public/favicon.svg +1 -0
  35. package/dist/public/fonts/Inter-LICENSE.txt +93 -0
  36. package/dist/public/fonts/Plex-LICENSE.txt +93 -0
  37. package/dist/public/fonts/inter-latin.woff2 +0 -0
  38. package/dist/public/fonts/plex-mono.woff2 +0 -0
  39. package/dist/public/icon.svg +1 -0
  40. package/dist/public/index.html +1 -0
  41. package/dist/public/landing.css +82 -0
  42. package/dist/public/landing.html +1 -0
  43. package/dist/public/landing.js +1 -0
  44. package/dist/public/logo/app-icon.svg +1 -0
  45. package/dist/public/logo/mark-green.svg +1 -0
  46. package/dist/public/logo/mark-monochrome.svg +1 -0
  47. package/dist/public/logo/mark-white.svg +1 -0
  48. package/dist/public/packages/core/src/subscriptions.js +22 -0
  49. package/dist/public/packages/receipt/src/index.js +90 -0
  50. package/dist/public/packages/ui/src/brand.js +5 -0
  51. package/dist/public/privacy.txt +45 -0
  52. package/package.json +7 -0
@@ -0,0 +1,63 @@
1
+ import {DatabaseSync} from 'node:sqlite';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import {applicationDirectory} from './security.js';
5
+ import {baseRecord,hash} from './metadata.js';
6
+ import {defaultSettings,emptyDiagnostics, } from './types.js';
7
+
8
+ function privateRecord(record ) {
9
+ const result=baseRecord(record.provider,'','');
10
+ for(const key of Object.keys(result) )if(Object.hasOwn(record,key))(result )[key]=record[key];
11
+ // These identities and project associations are not used by the V1 dashboard.
12
+ result.session_id=null;result.thread_id=null;result.project_identifier_hash=null;
13
+ const request=result.request_id;result.request_id=request===null?null:/^svh:[a-f0-9]{64}$/.test(request)?request:'svh:'+hash('request',request);
14
+ return result;
15
+ }
16
+ function privateCheckpoint(checkpoint ) {
17
+ const state=checkpoint.state;const diagnostics=emptyDiagnostics();
18
+ for(const key of Object.keys(diagnostics) )if(Object.hasOwn(state.diagnostics,key))(diagnostics )[key]=state.diagnostics[key];
19
+ // Codex's thread identity is needed to preserve its existing deduplication keys.
20
+ // Other identities/project fields are unnecessary for incremental token counters.
21
+ const next=state.thread!==undefined?{thread:state.thread,session:null,model:state.model,project:null,boundary:state.boundary,previous:state.previous===null?null:Object.fromEntries(['input_tokens','cached_input_tokens','cache_write_input_tokens','output_tokens','reasoning_output_tokens','total_tokens'].map(key=>[key,state.previous[key]??null])),turn:null,segment:state.segment,diagnostics}:{diagnostics};
22
+ return {ref:checkpoint.ref,size:checkpoint.size,mtime:checkpoint.mtime,offset:checkpoint.offset,line:checkpoint.line,head:checkpoint.head,tail:checkpoint.tail,state:next,identity:checkpoint.identity};
23
+ }
24
+ export class Store {
25
+ db ;
26
+ constructor(directory ,home ){const safe=applicationDirectory(directory,home);fs.mkdirSync(safe,{recursive:true,mode:0o700});const target=path.join(safe,'subvalue.sqlite');for(const file of [target,target+'-wal',target+'-shm',target+'-journal']){try{if(fs.lstatSync(file).isSymbolicLink())throw new Error('Database links are not allowed');}catch(e){if((e ).code!=='ENOENT')throw e;}}this.db=new DatabaseSync(target);this.db.exec(`
27
+ PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA busy_timeout=2000;
28
+ CREATE TABLE IF NOT EXISTS records(id TEXT PRIMARY KEY,provider TEXT NOT NULL,timestamp TEXT,data TEXT NOT NULL);
29
+ CREATE INDEX IF NOT EXISTS records_period ON records(timestamp,provider);
30
+ CREATE TABLE IF NOT EXISTS sources(ref TEXT PRIMARY KEY,checkpoint TEXT NOT NULL);
31
+ CREATE TABLE IF NOT EXISTS source_records(ref TEXT NOT NULL,id TEXT NOT NULL,PRIMARY KEY(ref,id));
32
+ CREATE TABLE IF NOT EXISTS app(key TEXT PRIMARY KEY,data TEXT NOT NULL);
33
+ PRAGMA user_version=1;
34
+ `);try{fs.chmodSync(target,0o600);}catch{/* Windows ACLs inherit from the user directory. */}
35
+ if(this.get('privacyMetadataVersion')!==1){this.db.exec('BEGIN IMMEDIATE');try{
36
+ for(const row of this.db.prepare('SELECT id,data FROM records').iterate())this.db.prepare('UPDATE records SET data=? WHERE id=?').run(JSON.stringify(privateRecord(JSON.parse(String(row.data)))),row.id );
37
+ for(const checkpoint of this.allCheckpoints())this.saveCheckpoint(checkpoint);
38
+ this.save('privacyMetadataVersion',1);this.db.exec('COMMIT');
39
+ }catch(error){this.db.exec('ROLLBACK');this.db.close();throw error;}}
40
+ }
41
+ checkpoint(ref ) {const r=this.db.prepare('SELECT checkpoint FROM sources WHERE ref=?').get(ref);return r?JSON.parse(String(r.checkpoint)):null;}
42
+ saveCheckpoint(c ){this.db.prepare('INSERT OR REPLACE INTO sources VALUES (?,?)').run(c.ref,JSON.stringify(privateCheckpoint(c)));}
43
+ clearSource(ref ){this.db.prepare('DELETE FROM source_records WHERE ref=?').run(ref);this.db.prepare('DELETE FROM sources WHERE ref=?').run(ref);}
44
+ put(r ,ref ) {
45
+ r=privateRecord(r);
46
+ const old=this.db.prepare('SELECT data FROM records WHERE id=?').get(r.id);let result ='new';
47
+ if(old){const prev=JSON.parse(String(old.data)) ;const usageKeys=['input_tokens','cached_input_tokens','cache_creation_tokens','cache_creation_5m_tokens','cache_creation_1h_tokens','output_tokens','reasoning_tokens','model_raw','request_id','total_tokens'] ;
48
+ const different=usageKeys.some(k=>prev[k]!==r[k]);result=different?'conflict':'duplicate';
49
+ if(different){prev.quality='INCOMPLETE';if(!prev.notes.includes('duplicate-usage-disagreement'))prev.notes.push('duplicate-usage-disagreement');this.db.prepare('UPDATE records SET data=? WHERE id=?').run(JSON.stringify(prev),r.id);}
50
+ }else this.db.prepare('INSERT INTO records VALUES (?,?,?,?)').run(r.id,r.provider,r.timestamp,JSON.stringify(r));
51
+ this.db.prepare('INSERT OR IGNORE INTO source_records VALUES (?,?)').run(ref,r.id);return result;
52
+ }
53
+ collectOrphans(){this.db.exec('DELETE FROM records WHERE id NOT IN (SELECT id FROM source_records)');}
54
+ records(from ,until ) {const rows=from&&until?this.db.prepare('SELECT data FROM records WHERE timestamp>=? AND timestamp<? ORDER BY timestamp,id').all(from,until):this.db.prepare('SELECT data FROM records ORDER BY timestamp,id').all();return rows.map(r=>JSON.parse(String(r.data)));}
55
+ revision() {return `${this.db.prepare('PRAGMA data_version').get() .data_version}:${this.db.prepare('SELECT total_changes() AS changes').get() .changes}`;}
56
+ allCheckpoints() {return this.db.prepare('SELECT checkpoint FROM sources').all().map(r=>JSON.parse(String(r.checkpoint)));}
57
+ settings() {const r=this.db.prepare("SELECT data FROM app WHERE key='settings'").get();return r?JSON.parse(String(r.data)):defaultSettings();}
58
+ saveSettings(s ){this.save('settings',s);}
59
+ save(key ,value ){this.db.prepare('INSERT OR REPLACE INTO app VALUES (?,?)').run(key,JSON.stringify(value));}
60
+ get (key ) {const r=this.db.prepare('SELECT data FROM app WHERE key=?').get(key);return r?JSON.parse(String(r.data)):null;}
61
+ statuses() {return this.get ('sources')??[];}
62
+ close(){this.db.close();}
63
+ }
@@ -0,0 +1,22 @@
1
+
2
+
3
+ export const subscriptionVerified='2026-10-05';
4
+ const openai='https://learn.chatgpt.com/docs/pricing',claude='https://claude.com/pricing';
5
+ export const subscriptionPlans =[
6
+ {id:'chatgpt-free',provider:'codex',name:'ChatGPT Free',monthly:0,interval:'monthly',source:openai},
7
+ {id:'chatgpt-go',provider:'codex',name:'ChatGPT Go',monthly:8,interval:'monthly',source:openai},
8
+ {id:'chatgpt-plus',provider:'codex',name:'ChatGPT Plus',monthly:20,interval:'monthly',source:openai},
9
+ ...[100,200,500].map(monthly=>({id:`chatgpt-pro-${monthly}`,provider:'codex' ,name:'ChatGPT Pro',monthly,interval:'monthly' ,source:openai})),
10
+ {id:'chatgpt-business-monthly',provider:'codex',name:'ChatGPT Business',monthly:25,interval:'monthly',perSeat:true,source:openai},
11
+ {id:'chatgpt-business-annual',provider:'codex',name:'ChatGPT Business',monthly:20,annual:240,interval:'annual',perSeat:true,source:openai},
12
+ {id:'claude-pro-monthly',provider:'claude',name:'Claude Pro',monthly:20,interval:'monthly',source:claude},
13
+ {id:'claude-pro-annual',provider:'claude',name:'Claude Pro',monthly:200/12,annual:200,interval:'annual',source:claude},
14
+ {id:'claude-max-5x',provider:'claude',name:'Claude Max 5x',monthly:100,interval:'monthly',source:'https://support.claude.com/en/articles/11049741-what-is-the-max-plan'},
15
+ {id:'claude-max-20x',provider:'claude',name:'Claude Max 20x',monthly:200,interval:'monthly',source:'https://support.claude.com/en/articles/11049741-what-is-the-max-plan'},
16
+ {id:'claude-team-standard-monthly',provider:'claude',name:'Claude Team · Standard seat',monthly:25,interval:'monthly',perSeat:true,source:claude},
17
+ {id:'claude-team-standard-annual',provider:'claude',name:'Claude Team · Standard seat',monthly:20,annual:240,interval:'annual',perSeat:true,source:claude},
18
+ {id:'claude-team-premium-monthly',provider:'claude',name:'Claude Team · Premium seat',monthly:125,interval:'monthly',perSeat:true,source:claude},
19
+ {id:'claude-team-premium-annual',provider:'claude',name:'Claude Team · Premium seat',monthly:100,annual:1200,interval:'annual',perSeat:true,source:claude}
20
+ ];
21
+ export function subscriptionPlan(provider ,id ) {return subscriptionPlans.find(p=>p.provider===provider&&p.id===id);}
22
+ export function subscriptionPlanLabel(p ) {return `${p.name} · $${p.interval==='annual'?p.annual+'/year':p.monthly+'/month'}${p.perSeat?' / seat':''}`;}
@@ -0,0 +1,55 @@
1
+
2
+ import {priceRecord,catalog, } from './pricing/index.js';
3
+ const dayFormatter=new Intl.DateTimeFormat('en-CA',{year:'numeric',month:'2-digit',day:'2-digit'});
4
+ function localDay(timestamp ) {const parts=dayFormatter.formatToParts(new Date(timestamp));return ['year','month','day'].map(type=>parts.find(part=>part.type===type) .value).join('-');}
5
+
6
+ export function dateRange(preset ,now=new Date(),from ,to ) {
7
+ const end=new Date(now.getFullYear(),now.getMonth(),now.getDate()+1);let start=new Date(now.getFullYear(),now.getMonth(),now.getDate()-29);let label='Last 30 days';
8
+ if(preset==='7d'){start=new Date(now.getFullYear(),now.getMonth(),now.getDate()-6);label='Last 7 days';}
9
+ else if(preset==='month'){start=new Date(now.getFullYear(),now.getMonth(),1);end.setTime(+new Date(now.getFullYear(),now.getMonth()+1,1));label=now.toLocaleDateString('en-US',{month:'long',year:'numeric'});}
10
+ else if(preset==='custom'){
11
+ if(!from||!to||!/^\d{4}-\d{2}-\d{2}$/.test(from)||!/^\d{4}-\d{2}-\d{2}$/.test(to))throw new Error('Choose a valid date range');
12
+ start=new Date(from+'T00:00:00');const last=new Date(to+'T00:00:00');const matches=(date ,text )=>Number.isFinite(+date)&&date.getFullYear()===Number(text.slice(0,4))&&date.getMonth()+1===Number(text.slice(5,7))&&date.getDate()===Number(text.slice(8,10));if(!matches(start,from)||!matches(last,to)||start>last||+last-+start>36600*864e5)throw new Error('Choose a valid date range');end.setTime(+new Date(last.getFullYear(),last.getMonth(),last.getDate()+1));label=`${from} — ${to}`;
13
+ }else if(preset!=='30d')throw new Error('Invalid period');
14
+ return {from:start.toISOString(),until:end.toISOString(),label};
15
+ }
16
+
17
+ export function subscriptionForRange(monthly ,range ) {
18
+ // Calendar-day proration in the server's local timezone, including multi-month ranges.
19
+ let total=0;const day=new Date(range.from),until=new Date(range.until);let guard=0;
20
+ while(day<until){const days=new Date(day.getFullYear(),day.getMonth()+1,0).getDate();total+=monthly/days;day.setDate(day.getDate()+1);if(++guard>36600)throw new Error('Range too large');}return total;
21
+ }
22
+
23
+
24
+
25
+
26
+ function neutral(reason ,subscription =null) {return {subscription,value:null,roi:null,breakEven:null,outcome:'neutral',reason};}
27
+ export function summarize(records ,statuses ,settings ,range ,c =catalog,demo=false) {
28
+ const selected=records.filter(r=>r.timestamp&&r.timestamp>=range.from&&r.timestamp<range.until);const prices=new Map(selected.map(r=>[r.id,priceRecord(r,c)]));const dailyMap=new Map ();
29
+ for(const r of selected){const date=localDay(r.timestamp );const d=dailyMap.get(date)??{date,codex:null,claude:null,hasUsage:true};const cost=prices.get(r.id) .usd;if(cost!==null)d[r.provider]=(d[r.provider]??0)+cost;dailyMap.set(date,d);}
30
+ const daily=[...dailyMap.values()].sort((a,b)=>a.date.localeCompare(b.date));
31
+ const providers=(['codex','claude'] ).map(provider=>{
32
+ const rows=selected.filter(r=>r.provider===provider),all=records.filter(r=>r.provider===provider),source=statuses.find(s=>s.provider===provider);const issues ={};
33
+ const costs=rows.map(r=>prices.get(r.id) );for(const p of costs)if(p.reason)issues[p.reason]=(issues[p.reason]??0)+1;
34
+ const undated=all.filter(r=>!r.timestamp).length;if(undated)issues['undated-events']=undated;
35
+ const priced=costs.filter(p=>p.usd!==null),subtotal=priced.length?priced.reduce((n,p)=>n+p.usd ,0):null;
36
+ const historyPartial=!demo; // Local transcripts cannot prove that all historical requests were retained.
37
+ const completePricing=rows.length>0&&priced.length===rows.length;const apiEquivalent=completePricing?subtotal:null;
38
+ const billing=settings.billing[provider];const subscription=billing.mode==='SUBSCRIPTION'&&billing.monthly!==null?subscriptionForRange(billing.monthly,range):null;
39
+ let comparison=neutral(billing.mode==='API'?'API usage':billing.mode==='MIXED'?'Mixed billing':billing.mode==='UNKNOWN'?'Billing not set':subscription===null?'Subscription price not set':!completePricing?'Pricing unavailable':'Partial history',subscription);
40
+ if(completePricing&&subscription!==null){const value=apiEquivalent -subscription;let accumulated=0;let breakEven =null;for(const d of daily){accumulated+=d[provider]??0;if(accumulated>=subscription){breakEven=d.date;break;}}comparison={subscription,value,roi:subscription>0?apiEquivalent /subscription:null,breakEven:historyPartial?null:breakEven,outcome:historyPartial?'neutral':value>0?'positive':value<0?'negative':'neutral',reason:historyPartial?'Observed usage · Partial history':null};}
41
+ const sumTokens=(key ) =>rows.length&&rows.every(r=>r[key]!==null)?rows.reduce((total,r)=>total+r[key] ,0):null;
42
+ const tokenBreakdown={input:sumTokens('input_tokens'),output:sumTokens('output_tokens'),cacheRead:sumTokens('cached_input_tokens')};
43
+ const tokensKnown=rows.filter(r=>r.total_tokens!==null);const timestamps=all.map(r=>r.timestamp).filter((t) =>!!t).sort();
44
+ const hasUsage=rows.some(r=>r.total_tokens!==0);const sourceIncomplete=source?.errors||source?.missingFiles||source?.diagnostics.malformed||source?.diagnostics.partialTail||source?.diagnostics.oversize||source?.diagnostics.unsupported;
45
+ return {provider,detected:source?.detected??false,visible:hasUsage||settings.include[provider],status:rows.length?(hasUsage?'USAGE_FOUND':'NO_USAGE'):'NO_DATA',records:rows.length,importedRecords:all.length,tokens:tokensKnown.length?tokensKnown.reduce((n,r)=>n+r.total_tokens ,0):null,tokensPartial:tokensKnown.length!==rows.length,tokenBreakdown,apiEquivalent,knownSubtotal:subtotal,pricedRecords:priced.length,priceCoverage:rows.length?priced.length/rows.length:null,confidence:!rows.length?'NO DATA':rows.some(r=>r.quality==='INCOMPLETE')?'INCOMPLETE':sourceIncomplete||undated||rows.some(r=>r.quality==='LOW')?'LOW':rows.some(r=>r.quality==='MEDIUM')?'MEDIUM':'HIGH',historyPartial,comparison,mode:billing.mode,monthly:billing.monthly,first:timestamps[0]??null,last:timestamps.at(-1)??null,days:new Set(timestamps.map(t=>t.slice(0,10))).size,issues} ;
46
+ });
47
+ const active=providers.filter(p=>p.records>0),pricedRecords=active.reduce((n,p)=>n+p.pricedRecords,0),count=selected.length;const known=active.filter(p=>p.knownSubtotal!==null);const knownSubtotal=known.length?known.reduce((n,p)=>n+p.knownSubtotal ,0):null;const apiEquivalent=count>0&&pricedRecords===count?knownSubtotal:null;
48
+ const subs=active.filter(p=>p.mode==='SUBSCRIPTION');let comparison=neutral(active.some(p=>p.mode==='API'||p.mode==='MIXED'||p.mode==='UNKNOWN')?'Subscription comparison unavailable':'Partial history');
49
+ if(subs.length&&subs.length===active.length&&subs.every(p=>p.comparison.value!==null)){const subscription=subs.reduce((n,p)=>n+p.comparison.subscription ,0);const value=apiEquivalent -subscription;let accumulated=0,breakEven =null;if(demo)for(const d of daily){accumulated+=(d.codex??0)+(d.claude??0);if(accumulated>=subscription){breakEven=d.date;break;}}comparison={subscription,value,roi:subscription>0?apiEquivalent /subscription:null,breakEven,outcome:demo?(value>0?'positive':value<0?'negative':'neutral'):'neutral',reason:demo?null:'Observed usage · Partial history'};}
50
+ else if(subs.length&&subs.length===active.length&&subs.every(p=>p.comparison.subscription!==null))comparison.subscription=subs.reduce((n,p)=>n+p.comparison.subscription ,0);
51
+ const confidence=active.length?['INCOMPLETE','LOW','MEDIUM','HIGH'].find(q=>active.some(p=>p.confidence===q)) :'NO DATA';
52
+ const groups=new Map ();for(const r of selected){const key=r.provider+':'+(r.model_raw??'Unknown model');let rows=groups.get(key);if(!rows){rows=[];groups.set(key,rows);}rows.push(r);}
53
+ const models=[...groups.values()].map(rows=>{const first=rows[0],p=prices.get(first.id) ;const priced=rows.filter(r=>prices.get(r.id) .usd!==null),known=rows.filter(r=>r.total_tokens!==null);const issues ={};for(const r of rows){const reason=prices.get(r.id) .reason;if(reason)issues[reason]=(issues[reason]??0)+1;}return {provider:first.provider,raw:first.model_raw??'Unknown model',canonical:p.canonical,records:rows.length,pricedRecords:priced.length,tokens:known.length?known.reduce((n,r)=>n+r.total_tokens ,0):null,pricedTokens:priced.some(r=>r.total_tokens!==null)?priced.reduce((n,r)=>n+(r.total_tokens??0),0):null,knownSubtotal:priced.length?priced.reduce((n,r)=>n+prices.get(r.id) .usd ,0):null,mappingConfidence:p.mapping?.confidence??null,mappingSource:p.mapping?.source??null,issues};});
54
+ return {range,providers,total:{apiEquivalent,knownSubtotal,records:count,pricedRecords,priceCoverage:count?pricedRecords/count:null,comparison},daily,confidence,historyPartial:!demo,unresolvedModels:[...new Set(selected.filter(r=>!prices.get(r.id) .canonical).map(r=>r.model_raw??'Unknown model'))],models,pricingVersion:c.version,demo};
55
+ }
@@ -0,0 +1,29 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+ export const defaultSettings = () => ({onboarded:false,currency:'USD',include:{codex:false,claude:false},billing:{codex:{mode:'UNKNOWN',monthly:null},claude:{mode:'UNKNOWN',monthly:null}}});
29
+ export const emptyDiagnostics = () => ({malformed:0,inherited:0,repeated:0,resets:0,oversize:0,unsupported:0,partialTail:false,first:null,last:null,records:0});
@@ -0,0 +1,90 @@
1
+
2
+ import {product} from '../../ui/src/brand.js';
3
+
4
+
5
+ export const money=(n ,signed=false) =>n===null?'Unavailable':`${signed&&n>=0?'+':''}${new Intl.NumberFormat('en-US',{style:'currency',currency:'USD'}).format(n)}`;
6
+ /** Compare only the displayed priced amount; preserve uncertainty and billing eligibility. */
7
+ export function comparisonForDisplay(s ) {
8
+ const comparison=s.total.comparison,amount=s.total.apiEquivalent??s.total.knownSubtotal;
9
+ if(amount===null||comparison.subscription===null)return comparison;
10
+ return {...comparison,value:comparison.value??amount-comparison.subscription,roi:comparison.roi??(comparison.subscription>0?amount/comparison.subscription:null)};
11
+ }
12
+ export function receiptModel(s ,generatedAt=new Date()) {
13
+ const rows =[];
14
+ for(const p of s.providers.filter(p=>p.records>0&&p.visible!==false)){
15
+ rows.push({left:p.provider==='codex'?'CODEX':'CLAUDE CODE',right:'',strong:true});
16
+ if(p.tokenBreakdown){
17
+ for(const [label,value] of [['Input Tokens',p.tokenBreakdown.input],['Output Tokens',p.tokenBreakdown.output],['Cache Reads',p.tokenBreakdown.cacheRead]] )rows.push({left:label,right:value===null?'Unknown':value.toLocaleString('en-US')});
18
+ }else if(p.tokens!==undefined)rows.push({left:p.tokensPartial?'Known Tokens':'Tokens',right:p.tokens===null?'Unknown':p.tokens.toLocaleString('en-US')});
19
+ const amount=p.apiEquivalent??p.knownSubtotal;
20
+ rows.push({left:'Estimated API Cost',right:amount===null?'—':money(amount)});
21
+ rows.push({left:'',right:''});
22
+ }
23
+ const amount=s.total.apiEquivalent??s.total.knownSubtotal;
24
+ rows.push({left:'Total API Equivalent',right:amount===null?'—':money(amount),strong:true});
25
+ const comp=comparisonForDisplay(s);if(comp.subscription!==null)rows.push({left:'Subscription Cost',right:money(comp.subscription)});
26
+ const value=comp.value;
27
+ if(value!==null)rows.push({left:'You Saved',right:money(value,true),accent:value>=0?'positive':'negative',strong:true});
28
+ // With partial pricing, compare only the printed known amount to the declared
29
+ // prorated subscription. The coverage footer stays visible; no outcome is inferred.
30
+ const multiple=comp.roi;
31
+ if(multiple!==null)rows.push({left:'',right:''},{left:'VALUE MULTIPLE',right:`${multiple.toFixed(2)}×`,accent:multiple>=1?'positive':'negative',strong:true});
32
+ if(comp.breakEven)rows.push({left:'BREAK-EVEN',right:new Date(comp.breakEven+'T12:00:00').toLocaleDateString('en-US',{month:'short',day:'numeric'}).toUpperCase()});
33
+ const outcome=comp.outcome==='positive'?'YOUR PLAN WAS WORTH IT':comp.outcome==='negative'?'API WOULD HAVE BEEN CHEAPER':'';
34
+ const coverage=s.total.apiEquivalent===null&&s.total.priceCoverage!==null?`${Math.round(s.total.priceCoverage*100)}% priced · `:'';
35
+ const date=(value )=>new Date(value).toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'});
36
+ const generated=generatedAt.toLocaleString('en-US',{month:'short',day:'numeric',year:'numeric',hour:'numeric',minute:'2-digit',hour12:true}).replace(' at ', ' ');
37
+ return {period:s.range.label.toUpperCase(),periodDetail:date(s.range.from)+' – '+date(new Date(Date.parse(s.range.until)-1).toISOString()),generated,rows,outcome,confidence:s.historyPartial?`${coverage}API equivalent · Partial history`:`${coverage}API equivalent · ${s.confidence==='HIGH'?'High confidence':'Incomplete'}`,demo:s.demo};
38
+ }
39
+ export async function receiptFonts() {if(typeof document!=='undefined'&&document.fonts)await Promise.all([document.fonts.load('14px "Plex Mono"'),document.fonts.load('600 14px "Plex Mono"')]);}
40
+ const paperTextures=new WeakMap ();
41
+ function paperTexture(canvas ) {
42
+ const document=canvas.ownerDocument;let texture=paperTextures.get(document);if(texture)return texture;
43
+ texture=document.createElement('canvas');texture.width=64;texture.height=64;
44
+ const context=texture.getContext('2d') ;const pixels=context.createImageData(64,64);let seed=619;
45
+ for(let i=0;i<64*64;i++){seed=(Math.imul(seed,1664525)+1013904223)>>>0;if(seed%7!==0)continue;const color=seed%2?255:0;pixels.data.set([color,color,color,color?4:8],i*4);}
46
+ context.putImageData(pixels,0,0);paperTextures.set(document,texture);return texture;
47
+ }
48
+ const rowHeight=(row )=>!row.left&&!row.right?34:!row.right?34:row.left==='VALUE MULTIPLE'?46:row.accent?32:28;
49
+ export function drawReceipt(canvas ,model ) {
50
+ const width=360,height=196+model.rows.reduce((n,row)=>n+rowHeight(row),0)+174,scale=6;
51
+ canvas.width=width*scale;canvas.height=height*scale;canvas.style.aspectRatio=`${width}/${height}`;
52
+ const c=canvas.getContext('2d');if(!c)throw new Error('Canvas unavailable');c.scale(scale,scale);
53
+ // The same clipped paper silhouette is used in preview and PNG, including both torn edges.
54
+ c.beginPath();c.moveTo(0,7);
55
+ for(let x=0;x<width;x+=10){const tooth=4+(x%3);c.lineTo(x+5,tooth);c.lineTo(x+10,8);}
56
+ c.lineTo(width,height-8);for(let x=width;x>0;x-=10){c.lineTo(x-5,height-4-(x%3));c.lineTo(x-10,height-8);}c.closePath();
57
+ const paper=c.createLinearGradient(0,0,width*.3,height);paper.addColorStop(0,'#181e24');paper.addColorStop(.48,'#131920');paper.addColorStop(1,'#11171c');c.fillStyle=paper;c.fill();c.save();c.clip();
58
+ c.fillStyle=c.createPattern(paperTexture(canvas),'repeat') ;c.fillRect(0,0,width,height);
59
+ c.restore();c.strokeStyle='#b4b8bd20';c.lineWidth=.6;c.stroke();
60
+ const mono='"Plex Mono", Consolas, monospace',left=24,right=width-24;
61
+ const text=(value ,x ,y ,size ,color='#e3e4e5',align ='left',bold=false,maxWidth=width-48)=>{
62
+ c.font=`${bold?'600 ':''}${size}px ${mono}`;while(c.measureText(value).width>maxWidth&&size>8){size-=.5;c.font=`${bold?'600 ':''}${size}px ${mono}`;}
63
+ c.fillStyle=color;c.textAlign=align;c.fillText(value,x,y);
64
+ };
65
+ const line=(y )=>{c.strokeStyle='#6c6f754f';c.lineWidth=1;c.setLineDash([4,5]);c.beginPath();c.moveTo(left,y);c.lineTo(right,y);c.stroke();c.setLineDash([]);};
66
+ text(product.name,width/2,64,29,'#f0f0ef','center');text('USAGE RECEIPT',width/2,92,13,'#b6b8bd','center');
67
+ if(model.demo)text('EXAMPLE DATA',width/2,112,10,'#a3a5a9','center');
68
+ text('Period',left,137,12.5,'#b4b5b9');text(model.periodDetail,right,137,12.5,'#d0d1d5','right',false,245);
69
+ text('Generated',left,158,12.5,'#b4b5b9');text(model.generated,right,158,12.5,'#d0d1d5','right',false,230);line(180);
70
+ let y=210;
71
+ for(const row of model.rows){const step=rowHeight(row);if(!row.left&&!row.right){line(y-9);y+=step;continue;}
72
+ const provider=!row.right,multiple=row.left==='VALUE MULTIPLE';const label=provider?(row.left==='CODEX'?'Codex':'Claude Code'):row.left;
73
+ const color=row.accent==='positive'?'#37ec85':row.accent==='negative'?'#ed9987':row.strong?'#e1e2e4':'#bdc0c6';
74
+ const valueSize=multiple?25:row.accent?22:row.strong?18:16;
75
+ c.font=`${valueSize}px ${mono}`;const valueWidth=c.measureText(row.right).width;
76
+ text(label,left,y,multiple?17:provider?17:row.strong?14:14,multiple?'#e1e2e4':color,'left',provider,provider?width-48:width-62-valueWidth);
77
+ text(row.right,right,y,valueSize,row.accent?color:'#e3e4e7','right',row.strong,160);y+=step;
78
+ }
79
+ line(y-3);text(model.outcome,width/2,y+27,10,model.outcome==='YOUR PLAN WAS WORTH IT'?'#37ec85':model.outcome==='API WOULD HAVE BEEN CHEAPER'?'#ed9987':'#b2b3b9','center');
80
+ text(model.confidence,width/2,y+(model.outcome?51:28),11,'#a9adb5','center');
81
+ const footerDivider=y+73;
82
+ c.strokeStyle='#767b8126';c.beginPath();c.moveTo(left,footerDivider);c.lineTo(right,footerDivider);c.stroke();
83
+ text(product.site,left,height-44,11.5,'#a9adb5');
84
+ // Decorative print detail only: fixed pattern, no symbology or user-derived information.
85
+ const pattern=[2,1,1,1,3,1,1,2,2,1,1,1,4,1,1,2,3,1,2,1,1,2,2,1,3,1,1,1,2,2,1,1,3,1,2,1,1,2,4,1,1,1,2,1,3,2,1,1,2,1,1,2,3,1,2,1,1,1,3,2,1,1,2,1];
86
+ const module=134/pattern.reduce((n,w)=>n+w,0);let bx=right-134;
87
+ const barcodeHeight=41,barcodeTop=footerDivider+(height-8-footerDivider-barcodeHeight)/2;
88
+ c.fillStyle='#b8bec8c9';for(const [i,w]of pattern.entries()){if(i%2===0)c.fillRect(bx,barcodeTop,w*module,barcodeHeight);bx+=w*module;}
89
+ }
90
+ export async function exportReceipt(canvas ) {const blob=await new Promise ((resolve,reject)=>canvas.toBlob(b=>b?resolve(b):reject(new Error('PNG export failed')),'image/png'));const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download=product.exportBasename+'.png';a.click();setTimeout(()=>URL.revokeObjectURL(url),5000);}
@@ -0,0 +1,5 @@
1
+ /** Display identity only. Package names and local data paths are intentionally independent. */
2
+ export const product = {name:'SubValue',site:'subvalue.dev',exportBasename:'subvalue-receipt'};
3
+ // Opposed measurement corners. No letter, currency sign or provider-derived geometry.
4
+ export const markPath = 'M4 18V4h14v4H8v10H4Zm10 6h10V14h4v14H14v-4Z';
5
+ export const productMark = `<svg viewBox="0 0 32 32" fill="currentColor" aria-hidden="true"><path d="${markPath}"/></svg>`;
@@ -0,0 +1,65 @@
1
+ /* Both the public page and local app use the same receipt renderer and tokens. */
2
+ @font-face{font-family:Inter;font-style:normal;font-weight:100 900;font-display:swap;src:url('/fonts/inter-latin.woff2') format('woff2')}
3
+ @font-face{font-family:'Plex Mono';font-style:normal;font-weight:400 700;font-display:swap;src:url('/fonts/plex-mono.woff2') format('woff2')}
4
+
5
+ :root{color-scheme:dark;--bg:#080a0c;--panel:#111418;--border:#25282d;--text:#eff0f1;--muted:#999fa6;--green:#37ec85;--red:#ed9987;--mono:"Plex Mono",Consolas,monospace;--sans:Inter,"Segoe UI",Arial,sans-serif}
6
+ *{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font-family:var(--sans);font-size:14px;-webkit-font-smoothing:antialiased}button,input,select{font:inherit}button,a,input,select,summary{-webkit-tap-highlight-color:transparent}button{cursor:pointer}a{color:inherit;text-decoration:none}button:disabled{opacity:.55;cursor:wait}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,summary:focus-visible{outline:2px solid var(--green);outline-offset:4px}svg{width:19px;height:19px;flex-shrink:0}h1,h2,h3,p{margin:0}h1{font-size:32px;letter-spacing:-1.3px;font-weight:550;line-height:1.3}h2{font-size:15px;font-weight:550;letter-spacing:-.15px}h3{font-size:16px}p{line-height:1.7;color:var(--muted)}.brand{display:flex;align-items:center;gap:11px;font-size:21px;font-weight:650;letter-spacing:-.65px;white-space:nowrap}.brand-mark{color:var(--green);font-family:var(--mono);font-size:40px;line-height:.7;font-weight:400}.topbar{max-width:1392px;margin:auto;height:84px;padding:0 40px;display:flex;align-items:center;gap:64px;border-bottom:1px solid var(--border)}.local-tag{font:9px var(--mono);color:var(--muted);border:1px solid var(--border);padding:4px 5px;margin-left:2px;letter-spacing:1px}.topbar nav{display:flex;align-self:stretch;gap:30px}.nav-item{background:none;border:0;color:var(--muted);display:flex;align-items:center;gap:8px;border-bottom:2px solid transparent;font-size:13px;padding:0 3px}.nav-item svg{display:none}.nav-item.active{color:var(--text);border-bottom-color:var(--green)}.nav-item:hover{color:var(--text)}.local-status{margin-left:auto;display:flex;gap:8px;align-items:center;font:10px var(--mono);color:var(--muted);white-space:nowrap}.status-dot{width:5px;height:5px;background:var(--green);border-radius:50%}.app-main{max-width:1392px;margin:auto;padding:38px 40px 32px;min-height:calc(100vh - 150px)}.page-heading{display:flex;justify-content:space-between;align-items:center;gap:20px;margin-bottom:22px}.eyebrow{font:9px var(--mono);letter-spacing:1.7px;color:var(--muted);display:block;margin-bottom:10px}.control-wrap{display:flex;align-items:center;gap:12px}.date-controls{display:flex;background:var(--panel);border:1px solid var(--border);padding:3px;border-radius:5px;gap:3px}.date-controls button{border:0;background:none;color:var(--muted);min-width:49px;min-height:32px;border-radius:3px;font-size:12px}.date-controls .selected{background:#2a342d;color:var(--text)}.custom-range{display:flex;gap:8px;align-items:end}.custom-range label{font-size:10px;color:var(--muted)}.custom-range input{display:block;padding:6px;width:125px;margin-top:4px}.coverage{margin-bottom:24px;border-bottom:1px solid var(--border);padding-bottom:14px;font-size:11px;max-width:100%}.coverage summary{display:flex;align-items:center;gap:8px;cursor:pointer;list-style:none;color:var(--muted)}.coverage summary svg{width:13px;height:13px}.details-label{font-size:10px;margin-left:auto}.details-label:after{content:' +';font-family:var(--mono)}.coverage[open] .details-label:after{content:' −'}.coverage-content{padding:18px 0 0;max-width:760px}.coverage-content p{font-size:12px;margin:12px 0}.coverage-content code{overflow-wrap:anywhere}.issue-group{display:flex;flex-direction:column;gap:6px;margin:10px 0;font-size:11px}.issue-group span{display:flex;gap:20px;max-width:470px;color:var(--muted)}.issue-group b{margin-left:auto}dl{margin:0;color:var(--muted);font-size:11px}dl div{display:flex;justify-content:space-between;gap:16px;padding:5px 0}dd{margin:0;color:var(--text);text-align:right}.overview-layout{display:grid;grid-template-columns:minmax(0,1fr) 310px;gap:26px;align-items:start}.overview-main{min-width:0}.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-bottom:22px}.metrics.single{grid-template-columns:minmax(230px,1fr)}.metric{background:var(--panel);border:1px solid var(--border);border-radius:5px;min-height:133px;padding:18px 17px;display:flex;flex-direction:column;min-width:0}.metric.api-metric{border-top:2px solid #72eb9e;padding-top:17px}.label{font-size:11px;color:var(--muted);font-weight:400}.metric strong{font-size:clamp(22px,2.4vw,35px);font-weight:520;letter-spacing:-1.2px;margin-top:14px;white-space:nowrap;font-variant-numeric:tabular-nums}.metric strong:has(.multiply){letter-spacing:-1px}.metric-detail{font-size:9px;color:var(--muted);margin-top:auto;padding-top:12px;line-height:1.4}.metric.positive strong{color:var(--green)}.metric.negative strong{color:var(--red)}.multiply{font-weight:350;padding-left:3px;color:var(--muted)}.panel{background:var(--panel);border:1px solid var(--border);border-radius:5px}.provider-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-bottom:22px}.provider-grid:has(>.provider-card:only-child){grid-template-columns:minmax(0,1fr)}.provider-card{padding:20px 23px}.provider-title{display:flex;align-items:center;gap:11px;margin-bottom:21px}.provider-logo{font-size:29px;line-height:1}.provider-logo.claude{color:#d2b49c}.badge{font:9px var(--mono);border:1px solid var(--border);border-radius:3px;padding:4px 6px;color:var(--muted);margin-left:4px}.badge.green{color:var(--green);border-color:#315440;background:#163020}.provider-values{display:grid;grid-template-columns:1.5fr 1fr;gap:20px}.provider-values>div{display:flex;flex-direction:column;gap:8px}.provider-values strong{font-size:19px;font-weight:450;letter-spacing:-.5px;font-variant-numeric:tabular-nums}.provider-values small{font-size:10px;color:var(--muted)}.chart{padding:23px 24px 16px}.panel-title{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:24px}.legend{display:flex;gap:13px;color:var(--muted);font-size:9px;flex-wrap:wrap}.legend span{display:flex;gap:6px;align-items:center}.legend i{height:5px;width:5px;border-radius:50%;background:var(--green)}.legend i.claude-dot{background:#8aadc2}.chart-svg{width:100%;display:block}.chart-dates{display:flex;justify-content:space-between;font:9px var(--mono);color:var(--muted);padding:8px 0 0 40px}.chart-empty{height:190px;display:flex;align-items:center;justify-content:center;position:relative;font:12px var(--mono);color:var(--muted)}.empty-chart-lines{position:absolute;inset:0;background:repeating-linear-gradient(to bottom,transparent,transparent 47px,#222c24 48px,transparent 49px);opacity:.6}.chart-empty>span:last-child{z-index:1;background:var(--panel);padding:8px 16px}.overview-bottom{display:flex;justify-content:space-between;align-items:center;margin-top:18px;gap:10px;font-size:10px;color:var(--muted)}.overview-bottom>span{display:flex;gap:6px;align-items:center}.overview-bottom svg{width:13px;height:13px}.text-button{display:inline-flex;align-items:center;justify-content:center;gap:7px;color:var(--muted);background:none;border:0;padding:8px 0;font-size:11px;min-height:44px}.text-button:hover{color:var(--text)}.text-button svg{width:13px;height:13px}.button{background:var(--green);color:#0b1b10;border:1px solid var(--green);border-radius:4px;display:inline-flex;gap:9px;align-items:center;justify-content:center;min-height:43px;padding:11px 20px;font-size:12px;font-weight:600}.button:hover{background:#91f7b4;border-color:#91f7b4}.button.small{min-height:32px;padding:7px 12px}.receipt-panel{padding:0 8px;display:flex;flex-direction:column;align-items:center;gap:15px;min-width:0}.receipt-kicker{display:flex;align-items:center;justify-content:space-between;width:100%;margin-bottom:3px}.receipt-kicker .label{font:8px var(--mono);letter-spacing:1px}.receipt-kicker svg{width:14px;height:14px;color:var(--muted)}.receipt-canvas{width:100%;max-width:440px;display:block;filter:drop-shadow(0 12px 16px #0005)}.export-button{width:100%;max-width:440px}.export-note{font:8px var(--mono);color:var(--muted)}.receipt-page-layout{display:grid;grid-template-columns:minmax(280px,440px) minmax(250px,440px);gap:88px;justify-content:center;margin-top:36px}.receipt-aside{padding-top:70px;display:flex;flex-direction:column;align-items:start;gap:14px}.receipt-aside .eyebrow{margin:0}.receipt-aside>strong{font-size:40px;letter-spacing:-1.5px;font-weight:500}.receipt-aside>span:not(.eyebrow){color:var(--muted);font-size:12px}.receipt-aside .coverage{width:100%;margin-top:12px}.privacy-note{font-size:11px;color:var(--muted)}.privacy-note summary{cursor:pointer}.privacy-note p{margin-top:10px;font-size:12px}.settings-layout{max-width:820px;margin-top:28px}.settings-section{padding:25px 27px;margin-bottom:15px}.settings-section h2{margin-bottom:23px;font-size:13px}.source-row{display:grid;grid-template-columns:1fr auto;align-items:center;gap:12px;padding:16px 0;border-top:1px solid var(--border)}.source-row>div{display:flex;flex-direction:column;gap:6px}.source-row strong{font-size:13px;font-weight:500}.source-state{font-size:10px;color:var(--muted)}.include-check{font-size:11px;display:flex;gap:8px;align-items:center;color:var(--muted)}input[type=checkbox]{accent-color:var(--green);width:15px;height:15px}.source-row details{grid-column:1/-1;width:100%;font-size:10px;color:var(--muted)}details summary{cursor:pointer}details summary::-webkit-details-marker{display:none}.source-row details dl{margin:12px 0}.billing-row{display:grid;grid-template-columns:120px 160px minmax(150px,1fr);gap:20px;align-items:center;margin-bottom:18px}.provider-setting-name{font-size:12px}input,select{border:1px solid #354139;background:#0d120f;color:var(--text);border-radius:4px;min-height:36px;padding:8px 10px;width:100%;outline:none}select{font-size:11px}.amount-field{display:flex;align-items:center;gap:7px;color:var(--muted);font-size:10px}.amount-field input{min-width:0;width:95px;text-align:right;font-variant-numeric:tabular-nums}.amount-field>span:last-child{white-space:nowrap}.compact-details{font-size:10px;color:var(--muted);margin-top:18px}.compact-details p{margin-top:8px;font-size:11px}.setting-line{display:flex;justify-content:space-between;gap:10px;color:var(--muted);font-size:12px}.setting-line strong{color:var(--text);font:12px var(--mono)}.privacy-tags{display:flex;flex-wrap:wrap;gap:25px;font-size:10px;color:var(--muted);margin-bottom:22px}.privacy-tags span{display:flex;gap:7px;align-items:center}.privacy-tags svg{color:var(--green);width:12px;height:12px}.settings-section>details{font-size:10px;color:var(--muted)}.settings-section>details p{font-size:11px;margin:10px 0}.settings-section code{font:10px var(--mono);line-height:1.8;overflow-wrap:anywhere}.form-actions{display:flex;justify-content:end;margin-top:22px}.app-footer{max-width:1312px;margin:0 auto 24px;display:flex;justify-content:space-between;align-items:center;font:9px var(--mono);color:#7e8f83;border-top:1px solid var(--border);padding-top:20px}.version{color:#647268;margin-left:4px}.demo-banner{background:#203128;color:var(--green);text-align:center;font:10px var(--mono);padding:8px;letter-spacing:.5px}.hidden{display:none!important}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.loading{min-height:90vh;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:20px}.loading p{font-size:12px}.empty-panel{padding:28px;margin-top:20px}.empty-panel h2{font-size:14px}dialog{width:min(540px,calc(100vw - 32px));background:#141b16;border:1px solid #354c3d;border-radius:8px;color:var(--text);padding:34px;box-shadow:0 24px 120px #0009}dialog::backdrop{background:#050906cc;backdrop-filter:blur(3px)}dialog>.brand-mark{display:block;margin:0 0 30px}dialog h2{font-size:26px;letter-spacing:-.8px;margin:16px 0 30px}dialog .button{width:100%;margin-top:24px}.found-sources>div{display:flex;justify-content:space-between;align-items:center;padding:18px 0;border-top:1px solid var(--border)}.found-sources strong{font-weight:500;font-size:13px}.found-sources span{display:flex;gap:10px;font:11px var(--mono);color:var(--muted)}.found-sources svg{width:14px;height:14px;color:var(--green)}.onboarding-skip{display:flex;margin:10px auto 0}dialog .billing-row{grid-template-columns:1fr 1fr;gap:12px;margin-top:15px;margin-bottom:30px}dialog .provider-setting-name{display:none}dialog .billing-row select{grid-column:1/-1}dialog .amount-field{grid-column:1/-1;justify-content:end}dialog h3{font-size:14px;font-weight:450;margin-top:20px}#toast{position:fixed;bottom:30px;left:50%;transform:translate(-50%,15px);background:#263b2d;border:1px solid #3d6248;color:var(--text);padding:12px 20px;border-radius:5px;font-size:12px;opacity:0;pointer-events:none;z-index:10;transition:opacity .15s,transform .15s;white-space:nowrap}#toast.show{opacity:1;transform:translate(-50%,0)}
7
+ @media(min-width:1400px){.metric strong{font-size:34px}}
8
+ @media(max-width:1100px){.topbar{padding:0 28px;gap:40px}.app-main{padding:30px 28px}.overview-layout{grid-template-columns:minmax(0,1fr) 265px;gap:20px}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.metric{min-height:116px}.metric strong{font-size:29px}.provider-grid{grid-template-columns:1fr}.app-footer{margin:0 28px 24px}.control-wrap{flex-direction:column;align-items:end}.receipt-page-layout{gap:45px}.receipt-aside>strong{font-size:32px}}
9
+ @media(max-width:760px){.topbar{height:69px;padding:0 20px;gap:24px}.brand{font-size:19px}.local-tag{display:none}.topbar nav{gap:17px}.nav-item{font-size:11px}.local-status{display:none}.app-main{padding:28px 20px}.page-heading{align-items:start;flex-wrap:wrap;gap:17px}.page-heading h1{font-size:28px}.eyebrow{font-size:8px}.overview-layout{grid-template-columns:1fr}.overview-layout .receipt-panel{max-width:390px;width:100%;justify-self:center;margin-top:17px;padding:0 16px}.metrics{gap:10px;margin-bottom:16px}.metric{padding:17px;min-height:126px}.metric strong{font-size:31px}.provider-grid{margin-bottom:16px}.chart{padding:19px 18px}.panel-title{margin-bottom:20px}.panel-title h2{font-size:12px}.legend{gap:9px;font-size:8px}.chart-empty{height:158px}.overview-bottom{font-size:8px}.overview-bottom .text-button{font-size:10px}.receipt-page-layout{grid-template-columns:1fr;gap:24px;margin-top:24px}.receipt-page-layout>.receipt-panel{max-width:390px;justify-self:center;width:100%}.receipt-aside{padding:12px 0 0;max-width:420px;margin:auto;width:100%}.receipt-aside>strong{font-size:28px}.app-footer{margin:0 20px 84px;font-size:8px}.settings-section{padding:22px 19px}.billing-row{grid-template-columns:1fr 155px;gap:12px}.amount-field{grid-column:2}.provider-setting-name{align-self:center}.privacy-tags{gap:15px}.custom-range{flex-wrap:wrap;justify-content:end}.custom-range input{width:117px}.date-controls button{min-width:47px;min-height:36px}.control-wrap{align-items:start}.coverage{font-size:10px;margin-bottom:19px}.topbar nav{position:fixed;bottom:0;left:0;right:0;height:64px;z-index:8;background:#101712;border-top:1px solid var(--border);justify-content:space-around;gap:0}.nav-item{display:flex;flex-direction:column;justify-content:center;gap:5px;flex:1;border-bottom:0;border-top:2px solid transparent;font-size:10px}.nav-item.active{border-top-color:var(--green);color:var(--green)}.nav-item svg{display:block;width:18px;height:18px}.metric-detail{font-size:9px}.include-check{font-size:10px}.source-row details{font-size:10px}dialog{padding:28px}#toast{bottom:78px}.empty-panel{padding:22px}}
10
+ @media(max-width:360px){.app-main{padding:24px 16px}.metric{padding:15px 13px}.metric strong{font-size:27px}.metrics{gap:8px}.date-controls button{min-width:46px}.provider-card{padding:19px}.receipt-panel{padding:0!important}.overview-bottom>span{font-size:7px}}
11
+ @media(prefers-reduced-motion:reduce){*{transition:none!important;scroll-behavior:auto!important}}
12
+
13
+ .chart-svg{height:auto}
14
+
15
+ /* A compact graphite workspace, matching the original SubValue reference. */
16
+ body{background:#080b0d;background-image:none}
17
+ .app-shell{max-width:1220px;margin:36px auto;background:#0b0d10;border:1px solid #2b343a;border-radius:12px;box-shadow:0 28px 85px #0008,0 1px 0 #ffffff08 inset;overflow:hidden}
18
+ .topbar{height:70px;padding:0 25px;gap:38px;background:#090b0d;border-bottom-color:#222a2f}
19
+ .brand{font-size:20px;gap:9px;letter-spacing:-.6px}.brand-mark{font-size:34px}.topbar nav{gap:25px}.nav-item{font-size:12px;padding:0 2px}
20
+ .header-period{margin-left:auto;display:flex;align-items:center;gap:12px}.local-status{margin-left:0;color:var(--green)}.local-status svg{width:17px}
21
+ .date-controls{background:#101519;border-color:#252e33;padding:2px}.date-controls button{min-height:34px;min-width:45px;font-size:11px}.date-controls .selected{background:#252a2e;color:#eff0f1}
22
+ .app-main{padding:23px 25px 25px;min-height:0}.page-heading{margin-bottom:13px}.page-heading h1{font-size:19px;letter-spacing:-.4px;font-weight:550}
23
+ .coverage{border:0;padding-bottom:0;margin-bottom:20px;font-size:10px}.coverage summary{gap:7px}.coverage-content{border:1px solid var(--border);background:#101619;border-radius:5px;padding:18px;margin-top:12px;max-width:100%}
24
+ .overview-layout{grid-template-columns:minmax(0,1fr) 320px;gap:22px}.metrics{gap:12px;margin-bottom:16px}.metrics.single{grid-template-columns:minmax(0,1fr)}
25
+ .metric{min-height:116px;padding:16px 14px;background:linear-gradient(120deg,#14171b,#101317);border-color:#25282d;box-shadow:0 1px 0 #ffffff03 inset;border-radius:6px}
26
+ .metric.api-metric{border-top:1px solid #353b40;padding-top:16px}.metric .label{display:flex;gap:7px;align-items:center;font-size:10px;white-space:nowrap;color:#b7c0c7}.metric .label svg{width:16px;height:16px;color:#a3afb7}.metric.positive .label svg,.metric:has(.multiply) .label svg{color:var(--green)}
27
+ .metric strong{font-size:28px;font-weight:550;letter-spacing:-.9px;margin-top:17px;line-height:1.1}.metric-detail{padding-top:11px;font-size:9px;line-height:1.5}.metric strong{white-space:normal;overflow-wrap:anywhere}.multiply{color:inherit;padding-left:1px}
28
+ .panel{border-radius:6px;border-color:#25282d;background:#111418;box-shadow:0 1px 0 #ffffff03 inset}.provider-grid{gap:12px;margin-bottom:16px}.provider-card{padding:20px}.provider-title{gap:10px;margin-bottom:18px}.provider-logo{font-size:31px;color:#e7ebed}.provider-title h2{font-size:14px}.badge{font:8px var(--sans);border-radius:20px;padding:4px 8px}.badge.green{background:#153e29;color:#65f3a2;border-color:#224b34}.provider-values{padding-left:41px;gap:15px}.provider-values .label{font-size:10px}.provider-values strong{font-size:19px;font-weight:500}.provider-values small{font-size:9px}
29
+ .chart{padding:20px 20px 16px}.panel-title{margin-bottom:20px}.panel-title h2{font-size:12px}.legend{font-size:9px;gap:13px}.chart-svg{min-height:170px}.legend i{background:var(--green)}.chart-dates{font:9px var(--sans);padding-top:3px}.empty-chart-lines{background:repeating-linear-gradient(to bottom,transparent,transparent 47px,#242c32 48px,transparent 49px)}
30
+ .overview-bottom{margin-top:12px;font-size:9px}.overview-bottom>span{gap:5px}.overview-bottom .text-button{font-size:10px}.receipt-panel{padding:0;gap:14px}.receipt-canvas{filter:drop-shadow(0 10px 14px #0008)}
31
+ .button{background:var(--green);border-color:var(--green);color:#092214;border-radius:5px;min-height:44px;font-size:12px;font-weight:650;box-shadow:0 1px 0 #a1ffc955 inset}.button:hover{background:#5df39d;border-color:#5df39d}.export-button svg{width:16px;height:16px}
32
+ .app-footer{margin:0 25px 18px;border-top:1px solid #20282d;padding-top:14px;font-size:8px;color:#89979f}.app-footer .version{color:#76838b}
33
+ .receipt-page-layout{gap:64px;margin-top:24px;grid-template-columns:minmax(280px,380px) minmax(250px,380px)}.receipt-aside{padding-top:45px}.receipt-aside>strong{font-size:34px}.receipt-aside .coverage{margin-top:12px}
34
+ .settings-layout{max-width:760px;margin-top:20px}.settings-section{background:#11171b;padding:24px}.settings-section h2{font-size:13px;margin-bottom:18px}input,select{background:#0c1115;border-color:#303b43;min-height:40px}.amount-field input{width:110px}.privacy-tags{margin-bottom:18px}
35
+ dialog{background:#131a1e;border-color:#34453c}dialog::backdrop{background:#030607d9;backdrop-filter:blur(4px)}.demo-banner{background:#171d1a;color:#88c5a0;font-size:9px}.model-coverage{display:grid;grid-template-columns:1.6fr 1fr 1fr;gap:8px;padding:12px 0;border-top:1px solid var(--border);font-size:10px;color:var(--muted)}.model-coverage code{overflow-wrap:anywhere;color:var(--text)}.model-coverage a{grid-column:1/-1;color:var(--green)}
36
+ @media(min-width:1400px){.metric strong{font-size:28px}}
37
+ @media(max-width:1270px){.app-shell{margin:24px}.topbar{gap:28px}.overview-layout{grid-template-columns:minmax(0,1fr) 255px;gap:18px}.metric strong{font-size:25px}}
38
+ @media(min-width:1001px) and (max-width:1100px){.metrics{grid-template-columns:repeat(4,minmax(0,1fr))}.metric{padding:15px 10px}.metric .label{font-size:9px;gap:5px}.metric .label svg{width:13px}.metric strong{font-size:22px}.provider-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.provider-card{padding:17px}.provider-title{flex-wrap:wrap}.provider-values{padding-left:0}}
39
+ @media(max-width:1000px){.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.topbar{gap:25px}.topbar nav{gap:18px}.overview-layout{grid-template-columns:minmax(0,1fr) 245px}.metric strong{font-size:29px}.provider-grid{grid-template-columns:1fr}.header-period{gap:8px}.local-status{display:none}}
40
+ @media(max-width:760px){body{background:#090d10;background-image:none}.app-shell{margin:0;border:0;border-radius:0;box-shadow:none;overflow:visible;background:none}.topbar{height:auto;min-height:120px;padding:18px 18px 13px;gap:14px;flex-wrap:wrap;background:#090b0d}.brand{font-size:20px}.header-period{flex-basis:100%;margin-left:0;flex-wrap:wrap}.date-controls{width:100%;padding:3px}.date-controls button{flex:1;min-height:39px;font-size:12px}.custom-range{width:100%;justify-content:start}.custom-range label{flex:1}.custom-range input{width:100%}.custom-range .button{min-height:39px}.app-main{padding:22px 18px 28px}.page-heading h1{font-size:19px}.page-heading{margin-bottom:12px}.coverage{margin-bottom:17px;font-size:10px}.overview-layout{grid-template-columns:1fr;gap:20px}.metrics{gap:10px;margin-bottom:14px}.metric{min-height:127px;padding:17px 15px}.metric.api-metric{padding-top:17px}.metric .label{font-size:10px}.metric strong{font-size:30px}.metric-detail{font-size:9px}.provider-grid{margin-bottom:14px}.provider-card{padding:19px}.provider-values{padding-left:42px}.provider-values strong{font-size:19px}.chart{padding:19px 16px 14px}.panel-title{gap:10px;margin-bottom:15px}.panel-title h2{font-size:11px}.legend{font-size:8px;gap:8px}.chart-svg{min-height:145px}.chart-dates{font-size:9px}.overview-bottom{font-size:8px}.overview-layout .receipt-panel{padding:0;max-width:360px;width:100%;margin-top:6px}.receipt-page-layout{grid-template-columns:1fr;gap:25px}.receipt-page-layout>.receipt-panel{max-width:360px}.receipt-aside{padding-top:0}.topbar nav{background:#0d1418;height:calc(65px + env(safe-area-inset-bottom));padding-bottom:env(safe-area-inset-bottom);border-top-color:#2a343a}.nav-item{min-height:62px;font-size:10px}.nav-item.active{color:var(--green);border-top-color:var(--green)}.nav-item svg{width:19px;height:19px}.app-footer{margin:0 18px calc(88px + env(safe-area-inset-bottom));padding-top:15px}.settings-section{padding:22px 18px}.billing-row{grid-template-columns:1fr 155px;gap:12px}.amount-field{grid-column:2}.privacy-tags{gap:16px}.model-coverage{grid-template-columns:1fr 1fr}.model-coverage code{grid-column:1/-1}#toast{bottom:calc(80px + env(safe-area-inset-bottom))}.export-button{min-height:46px}}
41
+ @media(max-width:360px){.topbar{padding-left:15px;padding-right:15px}.app-main{padding-left:15px;padding-right:15px}.metric{padding:15px 12px}.metric.api-metric{padding-top:15px}.metric .label{font-size:9px;gap:5px}.metric strong{font-size:27px}.metric .label svg{width:14px}.provider-title h2{font-size:13px}.provider-card{padding:17px}.provider-values{padding-left:0}.billing-row{grid-template-columns:1fr 142px}.privacy-tags{gap:12px}.receipt-panel{padding:0}}
42
+ .provider-logo svg{width:29px;height:29px}.metric:has(.multiply) .label svg{stroke-width:2.3}
43
+ @media(min-width:761px){.provider-grid:has(>.provider-card:only-child) .provider-card{display:grid;grid-template-columns:1fr 1.4fr;gap:20px;align-items:center;min-height:98px}.provider-grid:has(>.provider-card:only-child) .provider-title{margin:0;flex-wrap:wrap}.provider-grid:has(>.provider-card:only-child) .provider-values{padding-left:0}.provider-grid:has(>.provider-card:only-child) .provider-values>div{gap:8px}}
44
+ @media(max-width:760px){.date-controls button{min-height:44px}.custom-range input,.custom-range .button{min-height:44px}}
45
+
46
+ /* Neutral mark and original provider artwork stay separate from the UI wordmark. */
47
+ .brand-mark{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;line-height:1;flex-shrink:0}.brand-mark svg{width:100%;height:100%}.loading .brand-mark{width:32px;height:32px}.provider-logo{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;flex-shrink:0}.provider-logo img{width:29px;height:29px;object-fit:contain}.chart-svg g:hover rect{filter:brightness(1.15)}.chart-svg line{stroke-opacity:.65}.receipt-canvas{filter:drop-shadow(0 2px 2px #0007) drop-shadow(0 10px 14px #0006)}.export-button{box-shadow:0 1px 0 #b4ffcd55 inset,0 4px 14px #0003}.receipt-page-layout{grid-template-columns:minmax(280px,360px) minmax(250px,380px)}
48
+ @media(max-width:760px){.brand-mark{width:24px;height:24px}.receipt-page-layout{grid-template-columns:1fr}.metric{background:linear-gradient(120deg,#14171b,#101317)}.topbar nav{background:#101317}.overview-layout .receipt-panel,.receipt-page-layout>.receipt-panel{max-width:360px}.provider-values{padding-left:44px}}
49
+
50
+ /* Product identities: no generic company marks in provider cards. */
51
+ .codex-product-name{font-family:var(--mono);letter-spacing:-.4px!important;font-size:16px!important;font-weight:600!important}.claude-code-product-mark{width:30px;height:26px;object-fit:contain;flex-shrink:0}
52
+
53
+ .codex-product-mark{width:29px;height:29px;flex-shrink:0;filter:invert(1);opacity:.93}
54
+
55
+ /* Receipt text has room to read at dashboard scale, not just in the exported PNG. */
56
+ @media(min-width:1001px){.overview-layout{grid-template-columns:minmax(0,1fr) 360px}}
57
+ @media(min-width:761px) and (max-width:1000px){.overview-layout{grid-template-columns:minmax(0,1fr) 300px;gap:18px}}
58
+ @media(min-width:761px){.receipt-page-layout{grid-template-columns:minmax(280px,420px) minmax(250px,380px)}}
59
+ .billing-row{grid-template-columns:100px 145px minmax(0,1fr);gap:16px;align-items:start}.provider-setting-name{padding-top:12px}
60
+ .subscription-fields{display:grid;gap:10px;min-width:0}.subscription-fields select{width:100%;text-overflow:ellipsis}.amount-field input[readonly]{color:var(--muted)}
61
+ .subscription-fields.hidden{display:none}
62
+ .compact-details a{color:var(--green)}
63
+ @media(max-width:760px){.billing-row{grid-template-columns:1fr 155px}.subscription-fields{grid-column:1/-1}.subscription-fields .amount-field{grid-column:auto;justify-content:end}}
64
+ .refresh-button{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;padding:9px 13px;background:#151a1e;border:1px solid #343d43;border-radius:4px;color:var(--text);font-size:11px;white-space:nowrap}.refresh-button svg{width:15px;height:15px;color:var(--green)}.refresh-button:hover:enabled{background:#1d2328;border-color:#59656c}
65
+ dialog .subscription-fields{grid-column:1/-1}
@@ -0,0 +1 @@
1
+ import './apps/web/src/main.js';
@@ -0,0 +1,10 @@
1
+
2
+ import {product,productMark} from '../../../packages/ui/src/brand.js';
3
+ document.title=product.name+' — Know what your AI subscription is worth';
4
+ import {drawReceipt,receiptModel,receiptFonts} from '../../../packages/receipt/src/index.js';
5
+
6
+ const example={range:{from:new Date(2026,9,1).toISOString(),until:new Date(2026,10,1).toISOString(),label:'October 2026'},providers:[{provider:'codex',records:1,tokens:20530000,tokensPartial:false,apiEquivalent:201.36,knownSubtotal:201.36,comparison:{subscription:200}},{provider:'claude',records:1,tokens:7840000,tokensPartial:false,apiEquivalent:86.06,knownSubtotal:86.06,comparison:{subscription:20}}],total:{apiEquivalent:287.42,comparison:{subscription:220,value:67.42,roi:287.42/220,breakEven:'2026-10-24',outcome:'positive'}},historyPartial:false,confidence:'HIGH',demo:true} ;
7
+ document.querySelector('#landing') .innerHTML=`<header class="landing-nav"><a class="brand" href="#"><span class="brand-mark">${productMark}</span>${product.name}</a><a class="github-link" href="#release">Install <span>↓</span></a></header><main><section class="hero"><h1>${product.name}</h1><h2>Know what your AI<br class="desktop-break"> subscription is worth.</h2><p>See what your Codex and Claude Code usage would have cost via API.</p><div class="install-command"><code><span>$</span> npx @subvalue/cli</code><button class="copy-command" aria-label="Copy install command"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V4H4v12h4"/></svg></button></div><div class="trust-row"><span>✓ Local</span><span>✓ Read-only</span><span>✓ No telemetry</span></div><span class="release-note">Node.js 24.13+ · Windows · macOS · Linux</span></section><section class="product-preview" aria-label="Illustrative dashboard preview"><div class="preview-label"><span>PRODUCT PREVIEW</span><span>EXAMPLE DATA</span></div><div class="preview-window"><div class="preview-topbar"><span class="brand"><span class="brand-mark">${productMark}</span>${product.name}</span><span class="preview-tab">Overview</span><span>Receipt</span><span>Settings</span><span class="preview-date">October 2026</span></div><div class="preview-body"><div class="preview-main"><div class="preview-metrics"><div><span>API equivalent</span><strong>$287.42</strong></div><div><span>Subscription</span><strong>$220.00</strong></div><div><span>Value</span><strong class="green-text">+$67.42</strong></div><div><span>Value multiple</span><strong>1.31×</strong></div></div><div class="preview-providers"><div class="preview-provider"><span class="preview-provider-mark"><img class="codex-product-mark" src="/brands/codex-outline.svg" alt="" aria-hidden="true"></span><div><strong class="codex-product-name">Codex</strong><span>API equivalent</span></div><b>$201.36</b><span class="preview-active">Usage found</span></div><div class="preview-provider"><span class="preview-provider-mark"><img src="/brands/claude-code-clawd.svg" alt="" aria-hidden="true"></span><div><strong>Claude Code</strong><span>API equivalent</span></div><b>$86.06</b><span class="preview-active">Usage found</span></div></div><div class="preview-chart"><div><strong>Daily API equivalent</strong><span class="preview-legend"><span>● Codex</span><span class="claude">● Claude Code</span></span></div><div class="preview-bars" aria-hidden="true">${[15,24,19,34,29,38,21,25,39,26,32,28,43,25,31,38,49,44,52,36,41,29,45,40,53,45,66,51,57,49].map(h=>`<i style="height:${h}%"><span class="preview-codex-bar"></span><span class="preview-claude-bar"></span></i>`).join('')}</div><div class="preview-chart-axis"><span>Oct 1</span><span>Oct 15</span><span>Oct 31</span></div></div></div><div class="preview-receipt"><canvas id="example-receipt" role="img" aria-label="Illustrative ${product.name} receipt: API equivalent 287 dollars 42 cents, subscription 220 dollars, value 67 dollars 42 cents. Codex and Claude Code example data."></canvas><span>Export PNG ↓</span></div></div></div></section><section class="landing-privacy"><h2>Your data stays<br>on your machine.</h2><div><span>No account</span><span>No API keys</span><span>No uploads</span><span>No telemetry</span></div><details class="privacy-details"><summary>Details</summary><p>Usage is processed locally. Vercel hosts this page and can log website connections. <a href="/privacy.txt" target="_blank" rel="noreferrer">Data access ↗</a></p></details></section><section class="install-section" id="release"><div class="install-command"><code><span>$</span> npx @subvalue/cli</code><button class="copy-command" aria-label="Copy install command"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V4H4v12h4"/></svg></button></div><span class="release-note">Node.js 24.13+ · Windows · macOS · Linux</span></section></main><div class="copy-status" role="status" aria-live="polite"></div>`;
8
+ await receiptFonts();
9
+ drawReceipt(document.querySelector ('#example-receipt') ,receiptModel(example,new Date(2026,10,1,9,30)));
10
+ document.querySelectorAll ('.copy-command').forEach(b=>b.addEventListener('click',async()=>{try{await navigator.clipboard.writeText('npx @subvalue/cli');const status=document.querySelector('.copy-status') ;status.textContent='Copied';b.setAttribute('aria-label','Copied');setTimeout(()=>{status.textContent='';b.setAttribute('aria-label','Copy install command');},2000);}catch{const status=document.querySelector('.copy-status') ;status.textContent='npx @subvalue/cli';}}));
@@ -0,0 +1,47 @@
1
+
2
+ import {product,productMark} from '../../../packages/ui/src/brand.js';
3
+ document.title=product.name+' — Local usage';
4
+
5
+ import {subscriptionPlans,subscriptionPlan,subscriptionPlanLabel,subscriptionVerified} from '../../../packages/core/src/subscriptions.js';
6
+
7
+ import {drawReceipt,receiptModel,receiptFonts,exportReceipt,money,comparisonForDisplay} from '../../../packages/receipt/src/index.js';
8
+ const app=document.querySelector ('#app') ;
9
+ const name=(p )=>p==='codex'?'Codex':'Claude Code';
10
+ const esc=(s )=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c] ));
11
+ const icon=(key )=>{const paths ={bars:'M5 20V14 M12 20V8 M19 20V3',card:'M3 5h18v14H3z M3 9h18 M6 14h4',overview:'M3 10 12 3l9 7v10H3z M9 20v-7h6v7',receipt:'M6 3h12v18l-3-2-3 2-3-2-3 2z M9 8h6 M9 12h6',settings:'M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8 M12 2v3 M12 19v3 M2 12h3 M19 12h3 M5 5l2 2 M17 17l2 2 M5 19l2-2 M17 7l2-2',arrow:'M4 12h16 M14 6l6 6-6 6',download:'M12 3v12 M7 10l5 5 5-5 M4 17v4h16v-4',shield:'M12 3 4 6v6c0 5 8 9 8 9s8-4 8-9V6z',token:'M12 3 21 8v8l-9 5-9-5V8z M3 8l9 5 9-5 M12 13v8',check:'m5 12 4 4 10-10',refresh:'M20 7v5h-5 M4 17v-5h5 M6 6a8 8 0 0 1 13 2 M18 18a8 8 0 0 1-13-2'};return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="${paths[key]??paths.token}"/></svg>`;};
12
+ let settings ,sources =[],summary ,token='',demo=false,lastScan =null;let page=location.pathname==='/receipt'?'receipt':location.pathname==='/settings'?'settings':'overview';let period='month',from='',to='',onboardingStage='found';let busy=false;
13
+ async function api (url ,method='GET',body ) {const response=await fetch(url,{method,headers:{'Content-Type':'application/json','X-Subvalue-Token':token},body:body===undefined?undefined:JSON.stringify(body)});if(!response.ok)throw new Error('Unable to complete request');return response.json();}
14
+ function toast(text ){const el=document.querySelector ('#toast') ;el.textContent=text;el.classList.add('show');setTimeout(()=>el.classList.remove('show'),3000);}
15
+ let loadVersion=0;async function load(){const version=++loadVersion;const next=await api (`/api/summary?period=${period}${period==='custom'?`&from=${from}&to=${to}`:''}`);if(version!==loadVersion)return false;summary=next;return true;}
16
+ function metric(label ,value ,detail='',accent=''){const symbol=label==='Subscription'?'card':label==='Value multiple'?'bars':label.includes('value')||label==='Value'?'shield':'token';return `<article class="metric ${accent}"><span class="label">${icon(symbol)}${label}</span><strong>${value}</strong>${detail?`<span class="metric-detail">${detail}</span>`:''}</article>`;}
17
+ function navigation(){return `<header class="topbar"><a href="/" class="brand" aria-label="${esc(product.name)} overview"><span class="brand-mark">${productMark}</span>${esc(product.name)}</a><nav aria-label="Main navigation">${['overview','receipt','settings'].map(p=>`<button data-page="${p}" class="nav-item ${page===p?'active':''}" ${page===p?'aria-current="page"':''}>${icon(p)}<span>${p[0].toUpperCase()+p.slice(1)}</span></button>`).join('')}</nav><div class="header-period">${controls()}</div><span class="local-status" aria-label="Local only">${icon('shield')}</span></header>`;}
18
+ function controls(){return `<div class="date-controls" aria-label="Period">${[['7d','7D'],['30d','30D'],['month','Month'],['custom','Custom']].map(([v,l])=>`<button data-period="${v}" class="${period===v?'selected':''}" aria-pressed="${period===v}">${l}</button>`).join('')}</div>${period==='custom'?`<form id="custom-range" class="custom-range"><label>From<input type="date" name="from" value="${from}" required></label><label>To<input type="date" name="to" value="${to}" required></label><button class="button small">Apply</button></form>`:''}`;}
19
+ function coverage(){return `<details class="coverage"><summary>${icon('shield')}<span>${summary.historyPartial?'Partial history':summary.confidence==='HIGH'?'High confidence':'Incomplete'}${summary.total.priceCoverage!==null?` · ${Math.round(summary.total.priceCoverage*100)}% priced`:''}</span><span class="details-label">Details</span></summary><div class="coverage-content"><dl><div><dt>Confidence</dt><dd>${esc(summary.confidence)}</dd></div><div><dt>Usage events</dt><dd>${summary.total.records.toLocaleString()}</dd></div><div><dt>Priced events</dt><dd>${summary.total.pricedRecords.toLocaleString()}</dd></div><div><dt>Pricing version</dt><dd>${esc(summary.pricingVersion)}</dd></div></dl><p>Coverage describes imported records, not all requests ever made. Missing history stays unknown. Subscription amounts are prorated by calendar day.</p>${summary.providers.filter(p=>p.records||Object.keys(p.issues).length).map(p=>`<div class="issue-group"><strong>${name(p.provider)}</strong>${Object.entries(p.issues).map(([k,n])=>`<span>${esc(k.replaceAll('-',' '))} <b>${n.toLocaleString()}</b></span>`).join('')||'<span>All imported events priced</span>'}</div>`).join('')}${summary.unresolvedModels.length?`<p>Unresolved models: <code>${summary.unresolvedModels.map(esc).join(', ')}</code></p>`:''}${summary.models.map(m=>`<div class="model-coverage"><code>${esc(m.raw)}</code><span>${m.tokens===null?"Unknown tokens":m.tokens.toLocaleString()+" tokens"}</span><span>${m.pricedRecords}/${m.records} priced</span>${m.mappingSource?`<a href="${esc(m.mappingSource)}" target="_blank" rel="noreferrer">${esc(m.mappingConfidence)} mapping ↗</a>`:"<span>Mapping unavailable</span>"}</div>`).join('')}</div></details>`;}
20
+ function chart(){const daily=summary.daily;const values=daily.flatMap(d=>[d.codex,d.claude]).filter((n) =>n!==null);if(!values.length)return `<section class="panel chart"><div class="panel-title"><h2>Daily API equivalent</h2><span class="label">${summary.range.label}</span></div><div class="chart-empty"><span class="empty-chart-lines"></span><span>${summary.total.records?'No priced usage':'No data for this period'}</span></div></section>`;
21
+ const max=Math.max(...daily.map(d=>(d.codex??0)+(d.claude??0)),.01);const w=700,h=186,pad=30,barArea=620;const calendarDays=(Date.parse(daily.at(-1) .date)-Date.parse(daily[0].date))/864e5+1;const bars=daily.map(d=>{const step=barArea/calendarDays,bw=Math.min(18,step*.7),x=55+(Date.parse(d.date)-Date.parse(daily[0].date))/864e5*step;const a=(d.codex??0)/max*140,b=(d.claude??0)/max*140;return `<g><title>${esc(d.date)} · known API value ${money((d.codex??0)+(d.claude??0))}</title><rect x="${x}" y="${h-pad-a}" width="${bw}" height="${a}" rx="1" fill="url(#bar-green)"/><rect x="${x}" y="${h-pad-a-b}" width="${bw}" height="${b}" rx="1" fill="#8aadc2"/></g>`;}).join('');
22
+ const labels=[0,.5,1].map(f=>`<line x1="45" x2="690" y1="${h-pad-f*140}" y2="${h-pad-f*140}" stroke="#242a2e"/><text x="0" y="${h-pad-f*140+4}" fill="#899097" font-size="10">$${(max*f).toFixed(max<1?2:0)}</text>`).join('');
23
+ return `<section class="panel chart"><div class="panel-title"><h2>Daily API equivalent</h2><div class="legend">${summary.providers.some(p=>p.provider==='codex'&&p.records)?'<span><i></i>Codex</span>':''}${summary.providers.some(p=>p.provider==='claude'&&p.records)?'<span><i class="claude-dot"></i>Claude Code</span>':''}${summary.total.apiEquivalent===null?'<span>Known portion</span>':''}</div></div><svg viewBox="0 0 ${w} ${h}" class="chart-svg" role="img" aria-label="Daily known API-equivalent value"><defs><linearGradient id="bar-green" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#37ec85"/><stop offset="100%" stop-color="#278451"/></linearGradient></defs>${labels}${bars}</svg><div class="chart-dates"><span>${daily[0].date}</span><span>${daily.at(-1) .date}</span></div></section>`;}
24
+ function providerCards(){return `<div class="provider-grid">${summary.providers.filter(p=>p.visible).map(p=>`<article class="panel provider-card"><div class="provider-title">${p.provider==='claude'?'<img class="claude-code-product-mark" src="/brands/claude-code-clawd.svg" alt="" aria-hidden="true">':'<img class="codex-product-mark" src="/brands/codex-outline.svg" alt="" aria-hidden="true">'}<h2 class="${p.provider==='claude'?'claude-code-product-name':'codex-product-name'}">${name(p.provider)}</h2><span class="badge ${p.records?'green':''}">${p.status==='USAGE_FOUND'?'Usage found':p.status==='NO_USAGE'?'No usage':'No data'}</span></div><div class="provider-values"><div><span class="label">API equivalent</span><strong>${p.records?(p.knownSubtotal!==null?money(p.apiEquivalent??p.knownSubtotal):'—'):'No data'}</strong>${p.apiEquivalent===null&&p.knownSubtotal!==null?`<small>${Math.round((p.priceCoverage??0)*100)}% priced</small>`:''}</div><div><span class="label">${p.tokensPartial?'Known tokens':'Tokens'}</span><strong>${p.tokens===null?'Unknown':new Intl.NumberFormat('en-US',{notation:'compact',maximumFractionDigits:1}).format(p.tokens)}</strong></div></div></article>`).join('')}</div>`;}
25
+ function receiptPanel(compact=false){const model=receiptModel(summary);return `<section class="receipt-panel ${compact?'compact':''}" aria-label="AI usage receipt"><canvas class="receipt-canvas" id="receipt-canvas" role="img" aria-label="${esc(model.period+' · Generated '+model.generated+' · '+model.rows.map(r=>r.left+' '+r.right).join(' · ')+' · '+model.outcome+' · '+model.confidence)}"></canvas><button class="button export-button" id="export-png">${icon('download')}Export PNG</button></section>`;}
26
+ function refreshButton(){return `<button type="button" class="refresh-button" id="rescan" ${busy?'disabled':''} aria-busy="${busy}" title="${lastScan?'Last scan: '+esc(new Date(lastScan).toLocaleString()):'Scan local usage'}">${icon('refresh')}<span>${busy?'Scanning…':'Refresh usage'}</span></button>`;}
27
+ function updateRefreshButton(){const button=document.querySelector ('#rescan');if(button){button.disabled=busy;button.setAttribute('aria-busy',String(busy));button.innerHTML=icon('refresh')+'<span>'+(busy?'Scanning…':'Refresh usage')+'</span>';}}
28
+ function overview(){const t=summary.total,c=comparisonForDisplay(summary);const active=summary.providers.filter(p=>p.records);const showSubs=active.length&&active.every(p=>p.mode==='SUBSCRIPTION');return `<div class="page-heading"><div><h1>Overview</h1></div>${refreshButton()}</div>${coverage()}<div class="overview-layout"><div class="overview-main"><div class="metrics ${showSubs?'':'single'}">${metric('API equivalent',t.apiEquivalent===null?(t.knownSubtotal!==null?money(t.knownSubtotal):t.records?'—':'No data'):money(t.apiEquivalent),summary.range.label,'api-metric')}${showSubs?metric('Subscription',c.subscription===null?'Not set':money(c.subscription),period==='month'?'Per month':'Prorated'):''}${showSubs?metric(summary.historyPartial?'Observed value':'Value',c.value===null?'—':money(c.value,true),c.value===null?'Comparison unavailable':summary.historyPartial?'Partial history':'',c.value!==null?(c.value>=0?'positive':'negative'):''):''}${showSubs?metric('Value multiple',c.roi===null?'—':`${c.roi.toFixed(2)}<span class="multiply">×</span>`,c.breakEven?`Break-even ${new Date(c.breakEven+'T12:00:00').toLocaleDateString('en-US',{month:'short',day:'numeric'})}`:c.roi===null?'Unavailable':t.apiEquivalent===null?'Priced portion':''):''}</div>${providerCards()}${chart()}<div class="overview-bottom"><span>${icon('shield')}Local analysis. Read-only sources.</span><button class="text-button" data-page="settings">Manage sources ${icon('arrow')}</button></div>${!t.records?'<section class="panel empty-panel"><h2>No data for this period</h2><button class="text-button" data-page="settings">View sources →</button></section>':''}</div>${receiptPanel(true)}</div>`;}
29
+ function receiptPage(){return `<div class="page-heading"><div><h1>Receipt</h1></div></div><div class="receipt-page-layout">${receiptPanel()}<aside class="receipt-aside"><span class="eyebrow">API EQUIVALENT</span><strong>${summary.total.knownSubtotal!==null?money(summary.total.apiEquivalent??summary.total.knownSubtotal):summary.total.records?'No priced usage':'No data'}</strong><span>${summary.range.label}</span>${coverage()}<details class="privacy-note"><summary>Privacy</summary><p>Exports contain provider totals and the selected period. No projects, sessions, paths, prompts, or responses.</p></details></aside></div>`;}
30
+ function billingFields(p ){const b=settings.billing[p],plan=subscriptionPlan(p,b.planId);return `<div class="billing-row"><label class="provider-setting-name" for="mode-${p}">${name(p)}</label><select id="mode-${p}" name="mode-${p}" data-billing-mode="${p}"><option value="UNKNOWN" ${b.mode==='UNKNOWN'?'selected':''}>Not set</option><option value="SUBSCRIPTION" ${b.mode==='SUBSCRIPTION'?'selected':''}>Subscription</option><option value="API" ${b.mode==='API'?'selected':''}>API</option><option value="MIXED" ${b.mode==='MIXED'?'selected':''}>Mixed / not sure</option></select><div class="subscription-fields ${b.mode==='SUBSCRIPTION'?'':'hidden'}" id="amount-${p}"><label class="sr-only" for="plan-${p}">${name(p)} subscription plan</label><select id="plan-${p}" name="plan-${p}" data-subscription-plan="${p}"><option value="custom" ${!plan?'selected':''}>Custom amount</option>${subscriptionPlans.filter(plan=>plan.provider===p).map(option=>`<option value="${option.id}" ${plan?.id===option.id?'selected':''}>${esc(subscriptionPlanLabel(option))}</option>`).join('')}</select><label class="amount-field"><span class="sr-only">${name(p)} monthly subscription in USD</span><span>$</span><input name="monthly-${p}" type="number" min="0" max="1000000" step="0.01" value="${plan?plan.monthly.toFixed(2):b.monthly??''}" ${plan?'readonly':''} placeholder="Amount"><span>/ month</span></label></div></div>`;}
31
+ function settingsPage(){return `<div class="page-heading"><div><h1>Settings</h1></div></div><div class="settings-layout"><form id="settings-form"><section class="panel settings-section"><h2>Sources</h2>${summary.providers.map(p=>{const s=sources.find(s=>s.provider===p.provider);return `<div class="source-row"><div><strong>${name(p.provider)}</strong><span class="source-state">${p.status==='USAGE_FOUND'?'Usage found':p.detected?'No usage in selected period':'Not detected'}</span></div><label class="include-check"><input type="checkbox" name="include-${p.provider}" ${settings.include[p.provider]?'checked':''}>Always show</label><details><summary>Details</summary><dl><div><dt>Detection</dt><dd>${p.detected?'Detected':'Not detected'}</dd></div><div><dt>Imported events</dt><dd>${p.importedRecords.toLocaleString()}</dd></div><div><dt>Last scan</dt><dd>${lastScan?new Date(lastScan).toLocaleString():'Not scanned'}</dd></div><div><dt>Source files</dt><dd>${s?.files??0}</dd></div><div><dt>Available history</dt><dd>${p.first?p.first.slice(0,10)+' → '+p.last?.slice(0,10):'No data'}</dd></div><div><dt>Read errors</dt><dd>${s?.errors??0}</dd></div><div><dt>Malformed / skipped</dt><dd>${(s?.diagnostics.malformed??0)+(s?.diagnostics.oversize??0)}</dd></div><div><dt>Incomplete final line</dt><dd>${s?.diagnostics.partialTail?'Yes':'No'}</dd></div></dl></details></div>`;}).join('')}${refreshButton()}</section><section class="panel settings-section"><h2>Billing</h2>${(['codex','claude'] ).filter(p=>sources.find(s=>s.provider===p)?.detected).map(billingFields).join('')}<details class="compact-details"><summary>Details</summary><p>Plans verified ${subscriptionVerified}. ${subscriptionPlans.filter((p,i,list)=>list.findIndex(q=>q.source===p.source)===i).map(p=>`<a href="${esc(p.source)}" target="_blank" rel="noreferrer">${p.provider==='codex'?'ChatGPT':'Claude'} ↗</a>`).join(' · ')}. Annual plans use the exact annual price divided by 12; team plans are per seat. Enterprise / Edu: custom amount.</p><p>These are your declarations, not inferred billing history. Amounts are in USD and prorated by calendar day across the selected period. API and mixed usage have no subscription comparison.</p></details></section><section class="panel settings-section"><h2>Currency</h2><div class="setting-line"><span>API prices & subscription</span><strong>USD</strong></div></section><section class="panel settings-section"><h2>Privacy / data</h2><div class="privacy-tags"><span>${icon('check')}Local only</span><span>${icon('check')}Read-only sources</span><span>${icon('check')}No telemetry</span></div><details><summary>Data access</summary><p>Only approved Codex and Claude JSONL session files are streamed. Only normalized usage metadata is stored. Credentials, project contents and conversation content are not stored.</p><code>~/.codex/sessions/**/*.jsonl<br>~/.codex/archived_sessions/*.jsonl<br>~/.claude/projects/**/*.jsonl</code></details></section><div class="form-actions"><button class="button" type="submit">Save changes</button></div></form></div>`;}
32
+ function onboarding(){if(settings.onboarded)return '';const found=summary.providers.filter(p=>p.first),billable=summary.providers.filter(p=>p.records>0);return `<dialog id="onboarding" aria-labelledby="onboarding-title"><span class="brand-mark">${productMark}</span><span class="eyebrow">${esc(product.name.toUpperCase())} / FIRST RUN</span><h2 id="onboarding-title">${onboardingStage==='found'?(found.length?'Local usage found':'No local usage found'):'How did you use it?'}</h2>${onboardingStage==='found'?`<div class="found-sources">${summary.providers.map(p=>`<div><strong>${name(p.provider)}</strong><span>${p.first?`${Math.max(1,Math.ceil((Date.parse(p.last )-Date.parse(p.first))/864e5))} days ${icon('check')}`:p.detected?'No data found':'Not detected'}</span></div>`).join('')}</div><button class="button" id="onboarding-continue">Continue ${icon('arrow')}</button>`:`<form id="onboarding-billing">${billable.map(p=>`<h3>How did you use ${name(p.provider)}?</h3>${billingFields(p.provider)}`).join('')}${!billable.length?'<p>No usage metadata found.</p>':''}<button class="button" type="submit">Open overview ${icon('arrow')}</button></form>`}<button class="text-button onboarding-skip" id="onboarding-skip">${onboardingStage==='found'?'Skip billing':'Set up later'}</button></dialog>`;}
33
+ function render(){app.innerHTML=`${demo?'<div class="demo-banner">DEMO / FIXTURE DATA — no local sources accessed</div>':''}<div class="app-shell">${navigation()}<main class="app-main">${page==='overview'?overview():page==='receipt'?receiptPage():settingsPage()}</main><footer class="app-footer"><span>${esc(product.name)} <span class="version">0.1</span></span><span>LOCAL / READ-ONLY</span></footer></div>${onboarding()}`;
34
+ const canvas=document.querySelector ('#receipt-canvas');if(canvas)drawReceipt(canvas,receiptModel(summary));const dialog=document.querySelector ('#onboarding');if(dialog)dialog.showModal();bind();}
35
+ function readBilling(form ){const data=new FormData(form);for(const p of ['codex','claude'] ){const mode=data.get(`mode-${p}`);if(mode){const amount=data.get(`monthly-${p}`),plan=subscriptionPlan(p,String(data.get(`plan-${p}`)));settings.billing[p]={mode:mode ,monthly:mode==='SUBSCRIPTION'?(plan?.monthly??(amount!==''&&amount!==null?Number(amount):null)):null,...(mode==='SUBSCRIPTION'?{planId:plan?.id??null}:{})};}}}
36
+ async function save(){await api('/api/settings','POST',settings);await load();render();}
37
+ function bind(){document.querySelectorAll ('[data-page]').forEach(b=>b.addEventListener('click',()=>{page=b.dataset.page ;history.replaceState(null,'',page==='overview'?'/':`/${page}`);render();window.scrollTo(0,0);}));document.querySelectorAll ('[data-period]').forEach(b=>b.addEventListener('click',async()=>{if(busy)return;period=b.dataset.period ;if(period==='custom'&&(!from||!to)){from=new Date(summary.range.from).toLocaleDateString('en-CA');to=new Date(Date.parse(summary.range.until)-864e5).toLocaleDateString('en-CA');render();return;}try{if(await load())render();}catch{toast('Choose a valid date range');}}));
38
+ document.querySelector ('#custom-range')?.addEventListener('submit',async e=>{e.preventDefault();const data=new FormData(e.currentTarget );from=String(data.get('from'));to=String(data.get('to'));try{if(await load())render();}catch{toast('Choose a valid date range');}});
39
+ document.querySelectorAll ('[data-billing-mode]').forEach(select=>select.addEventListener('change',()=>document.querySelector(`#amount-${select.dataset.billingMode}`)?.classList.toggle('hidden',select.value!=='SUBSCRIPTION')));
40
+ document.querySelectorAll ('[data-subscription-plan]').forEach(select=>select.addEventListener('change',()=>{const p=select.dataset.subscriptionPlan ,plan=subscriptionPlan(p,select.value),input=document.querySelector (`[name="monthly-${p}"]`) ;input.readOnly=!!plan;if(plan)input.value=plan.monthly.toFixed(2);}));
41
+ document.querySelector ('#settings-form')?.addEventListener('submit',async e=>{e.preventDefault();const form=e.currentTarget ;readBilling(form);const data=new FormData(form);for(const p of ['codex','claude'] )settings.include[p]=data.has(`include-${p}`);try{await save();toast('Saved locally');}catch{toast('Unable to save settings');}});
42
+ document.querySelector('#export-png')?.addEventListener('click',async()=>{try{await exportReceipt(document.querySelector ('#receipt-canvas') );toast('Receipt exported');}catch{toast('Unable to export PNG');}});
43
+ document.querySelector('#rescan')?.addEventListener('click',async()=>{if(busy)return;busy=true;updateRefreshButton();try{await api('/api/rescan','POST');const poll=async()=>{try{const b=await api ('/api/bootstrap');if(b.scanning){setTimeout(()=>void poll(),1000);return;}sources=b.sources;lastScan=b.lastScan;busy=false;await load();render();toast('Sources updated');}catch{busy=false;updateRefreshButton();toast('Unable to scan');}};setTimeout(()=>void poll(),1000);}catch{busy=false;updateRefreshButton();toast('Unable to scan');}});
44
+ document.querySelector('#onboarding-continue')?.addEventListener('click',async()=>{if(!summary.providers.some(p=>p.records>0)){settings.onboarded=true;await save();return;}onboardingStage='billing';render();});document.querySelector('#onboarding-skip')?.addEventListener('click',async()=>{settings.onboarded=true;await save();});document.querySelector ('#onboarding-billing')?.addEventListener('submit',async e=>{e.preventDefault();readBilling(e.currentTarget );settings.onboarded=true;try{await save();}catch{toast('Unable to save');}});
45
+ }
46
+ async function boot(){try{const b=await api ('/api/bootstrap');settings=b.settings;sources=b.sources;token=b.token;demo=b.demo;lastScan=b.lastScan;await Promise.all([load(),receiptFonts(),document.fonts.load('14px Inter')]);render();}catch{app.innerHTML=`<main class="loading"><h1>${esc(product.name)}</h1><p>Unable to load local usage.</p><button class="button" id="retry">Retry</button></main>`;document.querySelector('#retry')?.addEventListener('click',()=>void boot());}}
47
+ void boot();
@@ -0,0 +1,14 @@
1
+ Codex outline product mark: installed official OpenAI.Codex Windows package 26.930.3930.0.
2
+ Archive: app/resources/app.asar; asset: webview/assets/codex_new-f14177b03534.svg.
3
+ Local file: codex-outline.svg. Exact original black SVG, displayed white using CSS inversion; geometry unchanged.
4
+ Product source: https://openai.com/codex/
5
+
6
+ Claude Code Clawd pixel mascot: installed official Anthropic VS Code extension anthropic.claude-code version 2.1.195, resources/clawd.svg.
7
+ Local file: claude-code-clawd.svg; exact SVG with original #D97757 color.
8
+ Official source: https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code
9
+
10
+ Unused Claude Code product lockup, ivory: official Anthropic press kit, Anthropic media resources/Anthropic logos/Claude logos/2 Claude Code logo/SVG/Claude Code logo - Ivory.svg.
11
+ Source: https://anthropic.com/press-kit, linked by https://www.anthropic.com/news
12
+ Resolved archive: https://www-cdn.anthropic.com/ae59ca4ca194dac9c9dc3bc78c5829468cb0e8af.zip
13
+
14
+ Retrieved 2026-10-04. Artwork preserved byte-for-byte. Marks belong to OpenAI and Anthropic respectively; not licensed under the project's MIT license. Used solely to identify providers, without implying endorsement.
@@ -0,0 +1,4 @@
1
+ <svg width="92" height="64" viewBox="0 0 92 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M65.92 0H51.9314L77.44 64H91.4286L65.92 0Z" fill="#FAF9F5"/>
3
+ <path d="M25.5086 0L0 64H14.2629L19.4798 50.56H46.166L51.3829 64H65.6457L40.1371 0H25.5086ZM24.0933 38.6743L32.8229 16.1829L41.5525 38.6743H24.0933Z" fill="#FAF9F5"/>
4
+ </svg>
@@ -0,0 +1,3 @@
1
+ <svg width="47" height="38" viewBox="0 0 47 38" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M5.08191 10.0769V0.938461H9.37422V10.0769H5.08191ZM9.23305 10.0769V0.938461H13.5254V10.0769H9.23305ZM13.3842 10.0769V0.938461H17.6765V10.0769H13.3842ZM17.5353 10.0769V0.938461H21.8276V10.0769H17.5353ZM21.6865 10.0769V0.938461H25.9788V10.0769H21.6865ZM25.8376 10.0769V0.938461H30.1299V10.0769H25.8376ZM29.9888 10.0769V0.938461H34.2811V10.0769H29.9888ZM34.1399 10.0769V0.938461H38.4322V10.0769H34.1399ZM38.291 10.0769V0.938461H42.5834V10.0769H38.291ZM0.930769 19.0769V9.93846H5.22308V19.0769H0.930769ZM5.08191 19.0769V9.93846H9.37422V19.0769H5.08191ZM9.23305 19.0769V14.5077H13.5254V19.0769H9.23305ZM13.3842 19.0769V9.93846H17.6765V19.0769H13.3842ZM17.5353 19.0769V9.93846H21.8276V19.0769H17.5353ZM21.6865 19.0769V9.93846H25.9788V19.0769H21.6865ZM25.8376 19.0769V9.93846H30.1299V19.0769H25.8376ZM29.9888 19.0769V9.93846H34.2811V19.0769H29.9888ZM34.1399 19.0769V14.5077H38.4322V19.0769H34.1399ZM38.291 19.0769V9.93846H42.5834V19.0769H38.291ZM42.4422 19.0769V9.93846H46.7345V19.0769H42.4422ZM5.08191 28.0769V18.9385H9.37422V28.0769H5.08191ZM9.23305 28.0769V18.9385H13.5254V28.0769H9.23305ZM13.3842 28.0769V18.9385H17.6765V28.0769H13.3842ZM17.5353 28.0769V18.9385H21.8276V28.0769H17.5353ZM21.6865 28.0769V18.9385H25.9788V28.0769H21.6865ZM25.8376 28.0769V18.9385H30.1299V28.0769H25.8376ZM29.9888 28.0769V18.9385H34.2811V28.0769H29.9888ZM34.1399 28.0769V18.9385H38.4322V28.0769H34.1399ZM38.291 28.0769V18.9385H42.5834V28.0769H38.291ZM5.08191 37.0769V27.9385H9.37422V37.0769H5.08191ZM13.3842 37.0769V27.9385H17.6765V37.0769H13.3842ZM29.9888 37.0769V27.9385H34.2811V37.0769H29.9888ZM38.291 37.0769V27.9385H42.5834V37.0769H38.291Z" fill="#D97757"/>
3
+ </svg>