/* Deep-space v2 tokens and components. Offline, standalone; no v1 imports.
   Existing v2-* selectors remain available alongside the v1 HTML class names. */
:root{
  color-scheme:dark;
  --v2-bg:#0c0f1f;
  --v2-card:#151a33;
  --v2-card-2:#111529;
  --v2-line:#232a4d;
  --v2-primary:hsl(234.620690,100%,73.5%);
  --v2-primary-dim:hsl(232.978723,43.925234%,66%);
  --v2-primary-hover:#8290ff;
  --v2-accent:#56d6e8;
  --v2-text:#e8ebf5;
  --v2-dim:#8e97b8;
  --v2-faint:hsl(226.363636,20.183486%,60.5%);
  --v2-purple-soft:#231e3f;
  --v2-teal-soft:#16283a;
  --v2-white:#fff;
  --v2-label:#a9b4d8;
  --v2-number:#f2f5ff;
  --v2-sub:#9fb0e8;
  --v2-active:#1a2040;
  --v2-rank-faint:hsl(231.111111,31.764706%,63.5%);
  --v2-purple:#b9a5ff;
  --v2-warning:#e4b66c;
  --v2-danger:#ff9aaa;
  --v2-danger-soft:#321e32;
  --v2-overlay-strong:rgba(12,15,31,.95);
  --v2-overlay-mid:rgba(12,15,31,.92);
  --v2-overlay-light:rgba(12,15,31,.91);
  --v2-overlay-clear:rgba(12,15,31,.9);
  --v2-empty-overlay:rgba(12,15,31,.9);
  --v2-nav-bg:rgba(12,15,31,.96);
  --v2-accent-clear:rgba(86,214,232,0);
  --v2-radius-lg:18px;
  --v2-radius-md:14px;
  --v2-radius-sm:8px;
  --v2-radius-brand:11px;
  --v2-radius-logo:7px;
  --v2-radius-tag:5px;
  --v2-radius-bar:2px;
  --v2-radius-round:50%;
  --v2-font:Inter,"SF Pro Display",-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
  --v2-mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;
  --v2-size-xs:10px;
  --v2-size-sm:11px;
  --v2-size-caption:12px;
  --v2-size-label:13px;
  --v2-size-control:14px;
  --v2-size-body:15px;
  --v2-size-input:16px;
  --v2-size-heading-sm:17px;
  --v2-size-heading:22px;
  --v2-size-rank:19px;
  --v2-size-count:24px;
  --v2-size-title:40px;
  --v2-size-title-mobile:30px;
  --v2-size-title-small:28px;
  --v2-size-hero-mobile:32px;
  --v2-size-metric:38px;
  --v2-size-metric-wide:40px;
  --v2-shadow-card:0 4px 20px rgba(0,0,0,.12);
  --v2-shadow-hover:0 8px 25px rgba(0,0,0,.2);
  --v2-shadow-primary:0 4px 14px rgba(110,123,255,.18);
  --v2-shadow-nav:0 6px 35px rgba(0,0,0,.24);
  --v2-shadow-accent:0 0 8px rgba(86,214,232,.85);
  --v2-shadow-number:0 2px 18px rgba(110,123,255,.45);
  --v2-shadow-pulse:0 0 7px rgba(110,123,255,.9);
  --v2-shadow-edge:0 0 10px rgba(86,214,232,.6);
  --v2-gradient-hero:linear-gradient(100deg,var(--v2-overlay-strong) 0%,var(--v2-overlay-mid) 42%,var(--v2-overlay-light) 72%,var(--v2-overlay-clear) 100%);
  --v2-gradient-accent:linear-gradient(90deg,var(--v2-accent),var(--v2-accent-clear));
  --v2-gradient-edge:linear-gradient(180deg,var(--v2-accent),var(--v2-primary));
  --v2-gradient-avatar:linear-gradient(130deg,var(--v2-purple-soft),var(--v2-teal-soft));
  --v2-space-xs:4px;
  --v2-space-sm:8px;
  --v2-space-md:12px;
  --v2-space-lg:16px;
  --v2-space-xl:24px;
  --v2-space-2xl:32px;
  --v2-touch:48px;
  --v2-focus-width:3px;
  --v2-motion-fast:.15s;
  --v2-motion-pulse:2.6s;
}

/* Shared foundations; namespaced colors avoid legacy token collisions. */
*{box-sizing:border-box}
html,body{margin:0;padding:0}
html{background:var(--v2-bg);color:var(--v2-text);font-family:var(--v2-font);line-height:1.6}
body,body.v2{background:var(--v2-bg);color:var(--v2-text);font-family:var(--v2-font);font-size:var(--v2-size-body);line-height:1.6;-webkit-font-smoothing:antialiased}
body.v2::before{content:'';position:fixed;inset:0;background:url('v2/mesh.webp') center top / cover no-repeat;opacity:.10;pointer-events:none;z-index:0}
a{color:var(--v2-sub);text-decoration:none;overflow-wrap:anywhere}
button,input,textarea,select{font:inherit}
button,a,input,textarea,select{-webkit-tap-highlight-color:transparent}
button,a{touch-action:manipulation}
button{cursor:pointer}
img,svg{max-width:100%}
h1{font-size:var(--v2-size-title);line-height:1.3;letter-spacing:-1.4px;font-weight:650;margin:9px 0 15px}
h2{font-size:var(--v2-size-heading);line-height:1.4;letter-spacing:-.5px;font-weight:630;margin:8px 0 12px}
h3{font-size:var(--v2-size-heading-sm);font-weight:630;margin:3px 0}
p{margin:8px 0 16px;overflow-wrap:anywhere}
small,.small{font-size:var(--v2-size-caption);color:var(--v2-dim)}
:focus-visible{outline:var(--v2-focus-width) solid var(--v2-accent);outline-offset:3px}
.sr-only{position:absolute;width:1px;height:1px;clip-path:inset(50%);overflow:hidden;white-space:nowrap}
.skip-link{position:absolute;top:-100px}
.skip-link:focus{top:8px;left:16px;background:var(--v2-card);color:var(--v2-text);padding:12px;min-height:var(--v2-touch);z-index:50}

/* Root template shell. Scope primary navigation so in-page navs stay in-flow. */
.app{position:relative;z-index:1;max-width:1184px;margin:0 auto;min-height:100dvh;padding:0 40px calc(104px + env(safe-area-inset-bottom))}
.app>header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:94px;border-bottom:1px solid var(--v2-line)}
.brand{display:flex;align-items:center;gap:10px;min-height:var(--v2-touch);font-size:var(--v2-size-heading-sm);font-weight:650;letter-spacing:-.4px;color:var(--v2-text)}
.brandmark{width:36px;height:36px;flex-shrink:0;display:inline-grid;place-items:center;background:var(--v2-card);border:1px solid var(--v2-line);border-radius:var(--v2-radius-brand);box-shadow:var(--v2-shadow-card)}
.brandmark svg{width:25px;height:25px}
.brand-text{line-height:1.4}
.brand small{display:block;font-size:var(--v2-size-xs);letter-spacing:1.8px;font-weight:500;line-height:1.3;color:var(--v2-faint)}
.header-meta{display:flex;align-items:center;gap:14px}
.header-note{font-size:var(--v2-size-sm);letter-spacing:1.2px;color:var(--v2-faint)}
.phase{font-size:var(--v2-size-sm);color:var(--v2-dim);background:var(--v2-card);border:1px solid var(--v2-line);border-radius:var(--v2-radius-sm);padding:5px 10px;white-space:nowrap}
main{max-width:800px;margin:0 auto;padding:44px 0 28px;min-width:0}
main.wide{max-width:none}
.app>nav{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);width:400px;max-width:calc(100% - 32px);display:flex;justify-content:space-around;padding:10px 12px calc(10px + env(safe-area-inset-bottom));gap:6px;background:var(--v2-nav-bg);border:1px solid var(--v2-line);border-radius:var(--v2-radius-lg);box-shadow:var(--v2-shadow-nav);backdrop-filter:blur(18px);z-index:20}
.app>nav a{display:flex;flex:1;align-items:center;flex-direction:column;justify-content:center;min-width:var(--v2-touch);min-height:var(--v2-touch);gap:3px;border-radius:var(--v2-radius-sm);font-size:var(--v2-size-sm);color:var(--v2-dim)}
.app>nav a[aria-current]{background:var(--v2-purple-soft);color:var(--v2-sub);font-weight:600}
.app>nav svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}

/* v1 component names: layout contracts preserved, deep-space surfaces applied. */
.card,.focus{border:1px solid var(--v2-line);border-radius:var(--v2-radius-lg);box-shadow:var(--v2-shadow-card)}
.card{display:block;padding:25px;background:var(--v2-card);margin:15px 0;color:var(--v2-text);min-width:0}
.card p{color:var(--v2-dim)}
.card>p:last-child{margin-bottom:0}
a.card{min-height:var(--v2-touch)}
a.card:hover{background:var(--v2-active);border-color:var(--v2-primary-dim);box-shadow:var(--v2-shadow-hover)}
.primary,.secondary,.text-button{display:flex;align-items:center;justify-content:center;gap:12px;min-height:var(--v2-touch);min-width:var(--v2-touch);border-radius:var(--v2-radius-md);font-size:var(--v2-size-control);font-weight:600;padding:12px 18px;line-height:1.6;text-align:center;white-space:normal;overflow-wrap:anywhere}
.primary,.secondary{width:100%;letter-spacing:.2px}
.primary{border:1px solid var(--v2-primary);background:var(--v2-primary);color:var(--v2-bg);box-shadow:var(--v2-shadow-primary);margin-top:20px}
.primary:hover{background:var(--v2-primary-hover);border-color:var(--v2-primary-hover)}
.primary span{margin-left:auto}
.secondary{border:1px solid var(--v2-primary-dim);background:var(--v2-card-2);color:var(--v2-sub);margin-top:14px}
.secondary:hover{background:var(--v2-active)}
.text-button{display:inline-flex;border:0;background:none;color:var(--v2-sub);font-weight:500}
.text-button:hover{background:var(--v2-active)}
.primary:disabled,.secondary:disabled,.text-button:disabled{opacity:1;cursor:not-allowed;background:var(--v2-card-2);color:var(--v2-dim);border-color:var(--v2-faint)}
.card-footer{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;border-top:1px solid var(--v2-line);margin-top:20px;padding-top:14px;color:var(--v2-dim);font-size:var(--v2-size-caption)}
.card-footer a{display:inline-flex;align-items:center;min-height:var(--v2-touch);min-width:var(--v2-touch)}
.card-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}
.card-grid .card{margin:0}
.card-grid.three{grid-template-columns:repeat(3,minmax(0,1fr))}
.empty-state{padding:32px;text-align:center;background:linear-gradient(var(--v2-empty-overlay),var(--v2-empty-overlay)),url('v2/mesh.webp') center / cover no-repeat;border:1px dashed var(--v2-faint);border-radius:var(--v2-radius-md);grid-column:1/-1;color:var(--v2-dim)}
.empty-state p{max-width:410px;margin:10px auto;font-size:var(--v2-size-label)}
.empty-state .secondary{max-width:270px;margin:20px auto 0}
.field{min-width:0;margin:16px 0}
label{display:block;font-size:var(--v2-size-control);font-weight:550;margin:20px 0 7px}
input:not([type=checkbox]):not([type=radio]):not([type=hidden]),textarea,select{display:block;width:100%;min-width:0;min-height:var(--v2-touch);font-size:var(--v2-size-input);border-radius:var(--v2-radius-sm);background:var(--v2-card-2);border:1px solid var(--v2-primary-dim);padding:11px 13px;color:var(--v2-text)}
input::placeholder,textarea::placeholder{color:var(--v2-dim);opacity:1}
input:focus,textarea:focus,select:focus{border-color:var(--v2-primary);background:var(--v2-active)}
/* Opaque disabled surfaces retain readable text, including WebKit's native fill. */
input:not([type=checkbox]):not([type=radio]):not([type=hidden]):disabled,textarea:disabled,select:disabled,button:disabled{background:var(--v2-card-2);color:var(--v2-dim);-webkit-text-fill-color:var(--v2-dim);border-color:var(--v2-faint);opacity:1}
input[aria-invalid=true]:not([type=checkbox]):not([type=radio]):not([type=hidden]),textarea[aria-invalid=true],select[aria-invalid=true]{border-color:var(--v2-danger)}
textarea{line-height:1.7;resize:vertical}
input[type=file]{font-size:var(--v2-size-control);overflow:hidden}
input[type=checkbox],input[type=radio]{width:20px;height:20px;margin:3px 0;accent-color:var(--v2-primary);flex-shrink:0}
.check,.field label:has(input[type=checkbox]),.field label:has(input[type=radio]){display:flex;align-items:center;gap:10px;min-height:var(--v2-touch);padding:10px 0;font-size:var(--v2-size-label);font-weight:400;color:var(--v2-dim);line-height:1.7;cursor:pointer}
.field-check{margin:16px 0}
.field-check label{margin:0;min-height:var(--v2-touch);padding:10px 0}
.field div:has(>div>label>input[type=checkbox]){padding:10px;background:var(--v2-card-2);border-radius:var(--v2-radius-sm)}
.field .errorlist{margin:6px 0}
.errorlist{color:var(--v2-danger);background:var(--v2-danger-soft);border-radius:var(--v2-radius-sm);padding:9px 12px 9px 28px;font-size:var(--v2-size-control);overflow-wrap:anywhere}
.tag{display:inline-block;padding:4px 9px;border-radius:var(--v2-radius-tag);background:var(--v2-teal-soft);font-size:var(--v2-size-sm);color:var(--v2-accent);font-weight:500}
a.tag,button.tag{display:inline-flex;align-items:center;justify-content:center;min-height:var(--v2-touch);min-width:var(--v2-touch)}
.avatar{width:43px;height:43px;flex-shrink:0;border-radius:var(--v2-radius-md);display:grid;place-items:center;background:var(--v2-gradient-avatar);color:var(--v2-purple);font-weight:600;font-size:var(--v2-size-heading-sm)}
.notice{padding:16px;border:1px solid var(--v2-primary-dim);border-radius:var(--v2-radius-md);background:var(--v2-teal-soft);color:var(--v2-text);font-size:var(--v2-size-label);margin:15px 0 22px;overflow-wrap:anywhere}
.notice p{color:var(--v2-text)}
.eyebrow{font-size:var(--v2-size-caption);font-weight:550;letter-spacing:1.2px;color:var(--v2-sub);margin:0 0 8px}
.section-heading{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;margin:34px 0 10px;gap:12px}
.section-heading h2{font-size:var(--v2-size-rank);margin:0}
.section-heading a{display:inline-flex;align-items:center;min-height:var(--v2-touch);min-width:var(--v2-touch);font-size:var(--v2-size-caption);flex-shrink:0}
.section-heading .subtle{color:var(--v2-dim)}
.row{display:flex;justify-content:space-between;align-items:center;gap:12px}
.row>div{min-width:0}
.row p{font-size:var(--v2-size-control);margin:0}
.row h3{margin:5px 0;overflow-wrap:anywhere}

/* Existing page helpers required when the two v1 stylesheets are absent. */
.intro{color:var(--v2-dim);font-size:var(--v2-size-body);margin-bottom:25px}
.focus{padding:25px;background:var(--v2-card-2);margin:22px 0}
.focus p,.quiet p{color:var(--v2-dim)}
.step{font-size:var(--v2-size-caption);letter-spacing:.5px;color:var(--v2-sub)}
.quiet{padding:8px 2px 18px}
.quiet p{font-size:var(--v2-size-label)}
.status-note{display:flex;gap:10px;align-items:flex-start;font-size:var(--v2-size-caption);color:var(--v2-dim);border-top:1px solid var(--v2-line);padding-top:18px;margin-top:30px}
.status-note p{margin:0}
.status-dot{width:7px;height:7px;background:var(--v2-warning);border-radius:var(--v2-radius-round);flex-shrink:0;margin-top:8px}
.back{display:inline-flex;align-items:center;min-height:var(--v2-touch);min-width:var(--v2-touch);padding:0 8px;font-size:var(--v2-size-caption)}
.link-arrow{font-size:var(--v2-size-label);color:var(--v2-sub)}
.hero{display:grid;grid-template-columns:1.12fr 1fr;min-height:326px;border:1px solid var(--v2-line);background:var(--v2-card);border-radius:var(--v2-radius-lg);position:relative;overflow:hidden;color:var(--v2-text);padding:42px}
.hero::before{content:'';position:absolute;inset:0;background:var(--v2-gradient-hero),url('v2/hero-nebula.webp') center 42% / cover no-repeat;pointer-events:none}
.hero-copy{position:relative;z-index:1;align-self:center;min-width:0}
.hero .eyebrow{color:var(--v2-label);letter-spacing:2px;font-size:var(--v2-size-xs)}
.hero h1{font-size:var(--v2-size-title);font-weight:570;letter-spacing:-1.3px;line-height:1.4}
.hero .intro{color:var(--v2-label);font-size:var(--v2-size-label);margin:16px 0 23px;max-width:355px}
.hero .primary{width:auto;display:inline-flex;min-width:190px;gap:28px;margin:0}
.hero-art{position:relative;width:100%;align-self:center;min-width:0}
.hero-foot{font-size:var(--v2-size-xs);color:var(--v2-dim);margin:14px 0 0}
.hero a.hero-history{display:inline-flex;align-items:center;color:var(--v2-sub);font-size:var(--v2-size-caption);margin-top:12px;min-height:var(--v2-touch)}
.workspace-line{display:flex;align-items:center;justify-content:space-between;padding:0 2px 15px;gap:10px;flex-wrap:wrap}
.workspace-line .eyebrow{margin:0}
.workspace-line span{font-size:var(--v2-size-sm);color:var(--v2-dim)}
.accent-dot{width:6px;height:6px;border-radius:var(--v2-radius-round);display:inline-block;background:var(--v2-accent);margin-right:6px}
.quick-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:18px}
.quick-card{display:flex;align-items:center;gap:20px;margin:0;overflow:hidden;min-height:133px;position:relative}
.quick-card .quick-copy{flex:1;min-width:0;z-index:1}
.quick-card h2{font-size:var(--v2-size-heading-sm);margin:6px 0}
.quick-card p{font-size:var(--v2-size-caption);margin:0;color:var(--v2-dim)}
.quick-art{width:106px;height:96px;object-fit:contain}
.quick-card .eyebrow{font-size:var(--v2-size-xs);letter-spacing:1.3px;color:var(--v2-sub)}
.quick-card .arrow{font-size:var(--v2-size-heading-sm);color:var(--v2-faint)}
.content-card{overflow:hidden;padding:0}
.content-card .card-body{padding:18px 21px}
.content-card h3{line-height:1.5}
.content-card p{font-size:var(--v2-size-caption)}
.content-cover{position:relative;height:170px;background:var(--v2-card-2);overflow:hidden;display:flex;align-items:center;justify-content:center}
.content-cover img{width:100%;height:100%;object-fit:contain;background:var(--v2-bg)}
.content-cover img.concept{background:var(--v2-gradient-avatar)}
.cover-label{position:absolute;bottom:10px;left:12px;font-size:var(--v2-size-xs);padding:4px 8px;color:var(--v2-text);background:var(--v2-nav-bg);border-radius:var(--v2-radius-tag)}
.play-badge{position:absolute;right:14px;bottom:12px;border-radius:var(--v2-radius-round);width:28px;height:28px;background:var(--v2-text);color:var(--v2-bg);display:grid;place-items:center;font-size:var(--v2-size-xs)}
.page-heading{display:flex;justify-content:space-between;align-items:center;gap:18px}
.heading-art{width:200px;max-width:30%;height:160px;object-fit:contain}
.action-pair{display:flex;gap:12px;align-items:stretch}
.action-pair>*{width:auto;flex:1;margin-top:0;min-width:0}
.customer-card h2{font-size:var(--v2-size-heading);margin-top:18px}
.customer-card p{font-size:var(--v2-size-label);min-height:44px}
.search{display:flex;gap:5px;margin:15px 0 20px}
.search .text-button{flex-shrink:0}
.channel-strip{display:flex;gap:8px;flex-wrap:wrap;margin:20px 0}
.channel-strip span{border:1px solid var(--v2-line);padding:7px 12px;border-radius:var(--v2-radius-sm);background:var(--v2-card);color:var(--v2-dim);font-size:var(--v2-size-caption)}
.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin:25px 0}
.metrics dt{font-size:var(--v2-size-caption);color:var(--v2-dim)}
.metrics dd{font-size:var(--v2-size-title-mobile);margin:6px 0;color:var(--v2-sub);font-weight:600;letter-spacing:-1px}
.metrics dd small{font-weight:400}
.review-guide{background:var(--v2-card-2);padding:17px;border-radius:var(--v2-radius-sm);margin:20px 0;font-size:var(--v2-size-label);color:var(--v2-dim)}
.review-guide p{margin:7px 0 0}
.form-pair{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.timeline-item{border-left:2px solid var(--v2-primary-dim);padding:0 0 20px 18px}
.task-line{display:flex;align-items:center;gap:12px}
.task-line .task-number{width:30px;height:30px;border:1px solid var(--v2-line);display:grid;place-items:center;border-radius:var(--v2-radius-sm);color:var(--v2-sub);font-size:var(--v2-size-caption);flex-shrink:0}
details{border-top:1px solid var(--v2-line);padding-top:9px;margin:24px 0 16px}
summary{cursor:pointer;font-size:var(--v2-size-control);color:var(--v2-sub);font-weight:550;min-height:var(--v2-touch);padding:10px 0}
.preserve{white-space:pre-line;overflow-wrap:anywhere}
.shot{display:flex;gap:14px;border-bottom:1px solid var(--v2-line);padding:16px 0}
.number{color:var(--v2-sub);font-weight:700}
.shot p{font-size:var(--v2-size-label);margin:5px 0}
.video{display:block;width:100%;max-width:420px;max-height:62dvh;aspect-ratio:9/16;background:var(--v2-bg);border-radius:var(--v2-radius-lg);box-shadow:var(--v2-shadow-card);margin:22px auto}
.inline{display:flex;gap:10px}
.empty{padding:26px 0;color:var(--v2-dim)}
.capture-text{white-space:pre-wrap;overflow-wrap:anywhere}
.capture-image{display:block;width:100%;max-height:60dvh;object-fit:contain;border-radius:var(--v2-radius-sm)}
audio{width:100%;margin:14px 0}

/* Shared collection summaries, filters and record components. */
.v2-app{position:relative;z-index:1;max-width:540px;margin:0 auto;min-height:100dvh;padding:0 16px calc(76px + env(safe-area-inset-bottom))}
.v2-top{display:flex;align-items:center;gap:9px;height:60px}
.v2-logo{width:22px;height:22px;border-radius:var(--v2-radius-logo);background:var(--v2-primary);flex-shrink:0}
.v2-brand{font-size:var(--v2-size-control);font-weight:500;letter-spacing:-.2px}
.v2-clock{margin-left:auto;font-size:var(--v2-size-caption);color:var(--v2-faint);font-family:var(--v2-mono)}
.v2-hero{position:relative;overflow:hidden;background:var(--v2-card);border:1px solid var(--v2-line);border-radius:var(--v2-radius-lg);padding:18px 17px;display:flex;align-items:flex-start;gap:12px;min-height:152px}
.v2-hero::before{content:'';position:absolute;inset:0;background:url('v2/hero-nebula.webp') center 42% / cover no-repeat;opacity:.92}
.v2-hero::after{content:'';position:absolute;inset:0;background:var(--v2-gradient-hero)}
.v2-hero>*{position:relative;z-index:1}
.v2-hero-main::after{content:'';display:block;width:64px;height:1px;margin-top:13px;background:var(--v2-gradient-accent)}
.v2-hero-main{min-width:0}
.v2-label{font-size:var(--v2-size-caption);color:var(--v2-label);letter-spacing:.3px;display:flex;align-items:center;gap:6px}
.v2-label::before{content:'';width:5px;height:5px;border-radius:var(--v2-radius-round);background:var(--v2-accent);box-shadow:var(--v2-shadow-accent);flex-shrink:0}
.v2-number{font-family:var(--v2-mono);font-size:var(--v2-size-metric);font-weight:500;letter-spacing:-1.5px;line-height:1.15;margin-top:3px;color:var(--v2-number);text-shadow:var(--v2-shadow-number)}
.v2-sub{font-size:var(--v2-size-caption);color:var(--v2-sub);margin-top:4px}
.v2-bars{margin-left:auto;align-self:flex-end;display:flex;align-items:flex-end;gap:3px;height:46px;flex-shrink:0;padding-top:6px}
.v2-bars span{width:8px;border-radius:var(--v2-radius-bar);background:var(--v2-primary-dim);display:block}
.v2-bars span:nth-last-child(1){background:var(--v2-accent)}
.v2-bars span:nth-last-child(2){background:var(--v2-primary)}
.v2-chips{display:flex;gap:7px;margin:13px 0;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.v2-chips::-webkit-scrollbar{display:none}
.v2-chip{flex-shrink:0;font-size:var(--v2-size-caption);padding:6px 12px;border-radius:var(--v2-radius-sm);background:var(--v2-card);color:var(--v2-dim);border:1px solid var(--v2-line);text-decoration:none;min-height:var(--v2-touch);min-width:var(--v2-touch);display:inline-flex;align-items:center;justify-content:center}
.v2-chip[aria-current]{background:var(--v2-primary);color:var(--v2-bg);border-color:var(--v2-primary)}
.v2-section{display:flex;align-items:baseline;justify-content:space-between;gap:12px;flex-wrap:wrap;margin:18px 2px 9px}
.v2-section h2{font-size:var(--v2-size-label);font-weight:500;margin:0;letter-spacing:.2px;display:flex;align-items:center;gap:7px}
.v2-section h2::after{content:'';width:4px;height:4px;border-radius:var(--v2-radius-round);background:var(--v2-primary);box-shadow:var(--v2-shadow-pulse);animation:v2pulse var(--v2-motion-pulse) ease-in-out infinite}
@keyframes v2pulse{0%,100%{opacity:.35}50%{opacity:1}}
.v2-section span{font-size:var(--v2-size-caption);color:var(--v2-faint)}
.v2-list{display:flex;flex-direction:column;gap:8px}
.v2-card{position:relative;display:flex;gap:11px;background:var(--v2-card);border:1px solid var(--v2-line);border-radius:var(--v2-radius-md);padding:12px 13px;min-height:var(--v2-touch);text-decoration:none;color:inherit;overflow:hidden}
.v2-card:first-child::before{content:'';position:absolute;left:0;top:12px;bottom:12px;width:2px;border-radius:var(--v2-radius-bar);background:var(--v2-gradient-edge);box-shadow:var(--v2-shadow-edge)}
.v2-card:active{background:var(--v2-active)}
.v2-rank{font-family:var(--v2-mono);font-size:var(--v2-size-rank);font-weight:500;line-height:1.1;color:var(--v2-primary);flex-shrink:0;padding-top:1px}
.v2-card:nth-child(n+4) .v2-rank{color:var(--v2-primary-dim)}
.v2-card:nth-child(n+7) .v2-rank{color:var(--v2-rank-faint)}
.v2-body{min-width:0;flex:1}
.v2-title{font-size:var(--v2-size-label);line-height:1.5;color:var(--v2-text);overflow-wrap:anywhere}
.v2-metas{display:flex;align-items:center;gap:7px;margin-top:6px;flex-wrap:wrap}
.v2-tag{font-size:var(--v2-size-sm);padding:2px 7px;border-radius:var(--v2-radius-tag);background:var(--v2-teal-soft);color:var(--v2-accent)}
.v2-tag.c-purple{background:var(--v2-purple-soft);color:var(--v2-purple)}
.v2-time{font-size:var(--v2-size-sm);color:var(--v2-faint);font-family:var(--v2-mono)}
.v2-empty{border:1px dashed var(--v2-faint);border-radius:var(--v2-radius-md);padding:26px 18px;text-align:center;font-size:var(--v2-size-label);color:var(--v2-faint);line-height:1.7;background:linear-gradient(var(--v2-empty-overlay),var(--v2-empty-overlay)),url('v2/mesh.webp') center / cover no-repeat}
.v2-empty a{display:inline-flex;align-items:center;min-height:var(--v2-touch);color:var(--v2-sub);text-decoration:none}
.v2-tabbar{position:fixed;left:0;right:0;bottom:0;max-width:540px;margin:0 auto;display:flex;align-items:center;min-height:calc(58px + env(safe-area-inset-bottom));padding-bottom:env(safe-area-inset-bottom);background:var(--v2-nav-bg);backdrop-filter:blur(16px);border-top:1px solid var(--v2-line);z-index:20}
.v2-tabbar a{flex:1;display:flex;align-items:center;justify-content:center;gap:5px;font-size:var(--v2-size-caption);color:var(--v2-faint);text-decoration:none;min-height:var(--v2-touch);min-width:var(--v2-touch)}
.v2-tabbar a[aria-current]{color:var(--v2-sub);font-weight:500}
.v2-dot{width:4px;height:4px;border-radius:var(--v2-radius-round);background:var(--v2-primary);display:none}
.v2-tabbar a[aria-current] .v2-dot{display:block}
.v2-note{font-size:var(--v2-size-sm);color:var(--v2-faint);border:1px solid var(--v2-line);border-radius:var(--v2-radius-sm);padding:9px 11px;margin:16px 0 4px;line-height:1.65}
.v2-note b{color:var(--v2-dim);font-weight:500}
.v2-rings{margin:18px 0 16px}
.v2-rings h2{margin:0;font-size:var(--v2-size-label);font-weight:500;color:var(--v2-text)}
.v2-rings-note{margin:3px 0 10px;font-size:var(--v2-size-sm);color:var(--v2-faint)}
.v2-rings-list{display:flex;gap:8px;overflow-x:auto;margin:0;padding:0 0 8px;list-style:none;scrollbar-width:thin;scrollbar-color:var(--v2-line) var(--v2-bg)}
.v2-rings-item{flex:1 0 84px;display:flex;flex-direction:column;align-items:center;gap:4px;padding:12px 8px;border:1px solid var(--v2-line);border-radius:var(--v2-radius-md);background:var(--v2-card);color:var(--v2-dim)}
.v2-rings-name{font-size:var(--v2-size-caption)}
.v2-rings-count{font:500 var(--v2-size-count)/1.2 var(--v2-mono);color:var(--v2-text)}
.v2-rings-state{font-size:var(--v2-size-sm)}
.v2-rings-time{font-size:var(--v2-size-xs);color:var(--v2-faint);white-space:nowrap}
.v2-rings-live{border-color:var(--v2-primary-dim)}
.v2-rings-live .v2-rings-count,.v2-rings-live .v2-rings-state{color:var(--v2-accent)}
.v2-rings-unwired{border-style:dashed;background:var(--v2-card-2)}
.v2-rings-unwired .v2-rings-count{color:var(--v2-faint)}
.v2-rings-unwired .v2-rings-state{padding:1px 6px;border:1px dashed var(--v2-faint);border-radius:var(--v2-radius-sm);color:var(--v2-dim)}

/* Only the agreed layout breakpoints; touch targets remain 48px on desktop. */
@media(min-width:1050px){
  .app>nav{position:absolute;top:24px;bottom:auto;width:360px;border-radius:var(--v2-radius-md);padding:5px 8px;box-shadow:none;background:transparent;border:0;backdrop-filter:none}
  .app>nav a{flex-direction:row;gap:8px;font-size:var(--v2-size-label)}
  .header-meta{margin-left:auto}
  .header-note{display:none}
  .app{padding-bottom:40px}
  .card-grid.three .content-cover{height:166px}
}
@media(min-width:760px){
  .v2-app{max-width:600px;padding-left:24px;padding-right:24px}
  .v2-tabbar{max-width:600px}
  .v2-number{font-size:var(--v2-size-metric-wide)}
}
@media(max-width:760px){
  .app{padding:0 22px calc(104px + env(safe-area-inset-bottom))}
  .app>header{height:76px}
  main{padding:28px 0 24px}
  .header-note{display:none}
  .brand{font-size:var(--v2-size-body)}
  .brandmark{width:33px;height:33px}
  .phase{font-size:var(--v2-size-xs)}
  .header-meta{gap:6px}
  .hero{grid-template-columns:1fr;padding:29px 26px 24px;min-height:0}
  .hero h1{font-size:var(--v2-size-hero-mobile);letter-spacing:-.8px;line-height:1.42}
  .hero .intro{max-width:270px}
  .hero-art{position:absolute;width:185px;right:-34px;top:5px;opacity:.33;pointer-events:none}
  .hero .primary{min-width:100%;justify-content:space-between}
  .quick-grid{gap:12px}
  .quick-card{padding:17px;align-items:flex-start;display:block;min-height:155px}
  .quick-art{position:absolute;width:65px;height:65px;right:-8px;top:1px;opacity:.65}
  .quick-card h2{font-size:var(--v2-size-input);margin-top:25px}
  .quick-card p{font-size:var(--v2-size-sm);max-width:135px}
  .quick-card .arrow{position:absolute;right:16px;top:16px;font-size:var(--v2-size-label)}
  .quick-card .eyebrow{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:105px}
  .card-grid.three{grid-template-columns:repeat(2,minmax(0,1fr))}
  .card-grid{gap:12px}
  .content-cover{height:145px}
  .content-card .card-body{padding:15px}
  .content-card h3{font-size:var(--v2-size-control)}
  .card-footer{font-size:var(--v2-size-sm)}
  .page-heading h1,h1{font-size:var(--v2-size-title-mobile);letter-spacing:-.7px}
  .heading-art{width:130px;max-width:27%;height:120px}
  .page-heading .intro{font-size:var(--v2-size-label)}
  .card{padding:21px}
  .content-card{padding:0}
  .card-grid .customer-card{padding:18px}
  .customer-card h2{font-size:var(--v2-size-heading-sm)}
  .customer-card p{font-size:var(--v2-size-caption)}
  .section-heading{margin-top:28px}
  .section-heading h2{font-size:var(--v2-size-heading-sm)}
  .form-card{padding:22px}
  .metrics{grid-template-columns:repeat(2,minmax(0,1fr))}
  .workspace-line span{font-size:var(--v2-size-xs)}
  .workspace-line .eyebrow{font-size:var(--v2-size-sm)}
}
@media(max-width:390px){
  .app{padding-left:17px;padding-right:17px}
  .hero{padding:26px 22px}
  .hero h1{font-size:var(--v2-size-title-small)}
  .hero-art{right:-49px;width:165px}
  .phase{padding:4px 7px}
  .brand{font-size:var(--v2-size-control)}
  .card-grid:not(.three){grid-template-columns:1fr}
  .quick-card{padding:14px}
  .content-card .card-body{padding:12px}
  .content-cover{height:120px}
  .heading-art{max-width:23%}
  .page-heading h1,h1{font-size:var(--v2-size-title-small)}
  .action-pair .primary,.action-pair .secondary{font-size:var(--v2-size-caption);padding:11px 12px}
  .card-footer{gap:6px}
  .section-heading a{font-size:var(--v2-size-sm)}
  .form-pair{grid-template-columns:1fr;gap:0}
}
@media(prefers-reduced-motion:no-preference){
  a.card{transition:box-shadow var(--v2-motion-fast),border-color var(--v2-motion-fast),background var(--v2-motion-fast)}
  .primary,.secondary,.text-button,.v2-card{transition:background var(--v2-motion-fast),box-shadow var(--v2-motion-fast)}
}
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
}

/* X2 contrast audit, 2026-10-20. Offline WCAG 2.1 AA; no layout changes.
   Validation: bash scripts/run_tests.sh all -> Ran 1626 tests, OK (skipped=23).
   Temporary test environment only: AIOS_MODE=local, AIOS_DB_NAME unset,
   AIOS_PLANNER_ENABLED/AIOS_WRITER_ENABLED/AIOS_DIRECTOR_ENABLED/
   AIOS_RETRIEVAL_ENABLED=0; PYTHONDONTWRITEBYTECODE=1, PYTHONUTF8=1.
   AIOS_TEST_TMP=$PWD/.x2-test-tmp; AIOS_RUNTIME=$AIOS_TEST_TMP/runtime;
   AIOS_FFMPEG/AIOS_FFPROBE use existing PATH executables;
   AIOS_FONT_PATH=C:/Windows/Fonts/msyh.ttc (read-only system test font).
   A local dummy Django secret was set in-process; no API credential was used.
   Initial isolated run lacked tools: failures=2/errors=81/skipped=23;
   adding PATH tools left failures=6/errors=24 (missing font); font resolved all.
   Test fixtures were not edited. Scratch cleanup was blocked by automatic tool
   approval ("blocked by policy"); .x2-test-tmp remains as test output, not source.
   Scope: 13 foreground tokens, 70 text pairs, 14 non-text pairs; all pass AFTER.
   Four foreground tokens failed on solid surfaces. H and S are preserved;
   only HSL lightness increases (hex equivalents below are rounded for display).
   Including conservative image bounds and disabled states: 10/13 tokens had
   failing contexts BEFORE. Image bounds are proofs, not measured screen pixels.
   Worst solid text: rank-faint/active 1.6558 -> 4.9530; minimum AFTER 4.6310.
   Text whose solid colors already passed is fixed by stronger hero scrims or
   opaque disabled surfaces. Error red, notice text and 3px focus already passed.
   Ordinary --v2-line separators, bars, shadows, dots and unused --v2-white are
   decorative/non-text and excluded; empty-state borders are checked at 3:1.
   Native disabled BEFORE colors are UA-dependent, so no invented ratio is given.
   Full result and runnable script live here because only this CSS is writable.
   Re-run from repository root (standard-library Python; emits table + assertions):
   python -B -c "from pathlib import Path; s=Path('static/studio/design-system.css').read_text(encoding='utf-8'); exec(s.split('X2_AUDIT_PY_BEGIN\n',1)[1].split('\nX2_AUDIT_PY_END',1)[0])"

FOREGROUND TOKEN / SELECTOR INVENTORY (inherited text uses its ancestor role)
--v2-accent = #56d6e8 [#56d6e8]
  .tag; .v2-tag; .v2-rings-live .v2-rings-count,.v2-rings-live .v2-rings-state
--v2-bg = #0c0f1f [#0c0f1f]
  .primary; .play-badge; .v2-chip[aria-current]
--v2-danger = #ff9aaa [#ff9aaa]
  .errorlist
--v2-dim = #8e97b8 [#8e97b8]
  small,.small; .phase; .app>nav a; .card p; .primary:disabled,.secondary:disabled,.text-button:disabled; .card-footer; .empty-state; input::placeholder,textarea::placeholder; input:not([type=checkbox]):not([type=radio]):not([type=hidden]):disabled,textarea:disabled,select:disabled,button:disabled; .check,.field label:has(input[type=checkbox]),.field label:has(input[type=radio]); .section-heading .subtle; .intro; .focus p,.quiet p; .status-note; .hero-foot; .workspace-line span; .quick-card p; .channel-strip span; .metrics dt; .review-guide; .empty; .v2-chip; .v2-note b; .v2-rings-item; .v2-rings-unwired .v2-rings-state
--v2-faint = hsl(226.363636,20.183486%,60.5%) [#868faf]
  .brand small; .header-note; .quick-card .arrow; .v2-clock; .v2-section span; .v2-time; .v2-empty; .v2-tabbar a; .v2-note; .v2-rings-note; .v2-rings-time; .v2-rings-unwired .v2-rings-count
--v2-label = #a9b4d8 [#a9b4d8]
  .hero .eyebrow; .hero .intro; .v2-label
--v2-number = #f2f5ff [#f2f5ff]
  .v2-number
--v2-primary = hsl(234.620690,100%,73.5%) [#7884ff]
  .v2-rank
--v2-primary-dim = hsl(232.978723,43.925234%,66%) [#828bce]
  .v2-card:nth-child(n+4) .v2-rank
--v2-purple = #b9a5ff [#b9a5ff]
  .avatar; .v2-tag.c-purple
--v2-rank-faint = hsl(231.111111,31.764706%,63.5%) [#848dbf]
  .v2-card:nth-child(n+7) .v2-rank
--v2-sub = #9fb0e8 [#9fb0e8]
  a; .app>nav a[aria-current]; .secondary; .text-button; .eyebrow; .step; .link-arrow; .hero a.hero-history; .quick-card .eyebrow; .metrics dd; .task-line .task-number; summary; .number; .v2-sub; .v2-empty a; .v2-tabbar a[aria-current]
--v2-text = #e8ebf5 [#e8ebf5]
  html; body,body.v2; .skip-link:focus; .brand; .card; input:not([type=checkbox]):not([type=radio]):not([type=hidden]),textarea,select; .notice; .notice p; .hero; .cover-label; .v2-title; .v2-rings h2; .v2-rings-count

FULL TABLE: token | foreground before -> after | background (after bounds) | before -> after | AA
--v2-text | #e8ebf5 -> #e8ebf5 | bg (#0c0f1f) | 15.9756 -> 15.9756 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | card (#151a33) | 14.3656 -> 14.3656 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | card-2 (#111529) | 15.1601 -> 15.1601 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | active (#1a2040) | 13.3123 -> 13.3123 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | teal-soft (#16283a) | 12.6065 -> 12.6065 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | purple-soft (#231e3f) | 13.2684 -> 13.2684 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | danger-soft (#321e32) | 12.8954 -> 12.8954 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | body-image (#0b0e1c..#242735) | 12.4312 -> 12.4312 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | hero-image (#0b0e1c..#242735) | 1.0851 -> 12.4312 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | empty-image (#0b0e1c..#242735) | 12.4312 -> 12.4312 | 4.5
--v2-text | #e8ebf5 -> #e8ebf5 | nav-image (#0c0e1e..#161928) | 14.6976 -> 14.6976 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | bg (#0c0f1f) | 6.5857 -> 6.5857 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | card (#151a33) | 5.9220 -> 5.9220 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | card-2 (#111529) | 6.2495 -> 6.2495 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | active (#1a2040) | 5.4878 -> 5.4878 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | teal-soft (#16283a) | 5.1968 -> 5.1968 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | purple-soft (#231e3f) | 5.4697 -> 5.4697 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | danger-soft (#321e32) | 5.3159 -> 5.3159 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | body-image (#0b0e1c..#242735) | 5.1246 -> 5.1246 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | hero-image (#0b0e1c..#242735) | 1.0000 -> 5.1246 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | empty-image (#0b0e1c..#242735) | 5.1246 -> 5.1246 | 4.5
--v2-dim | #8e97b8 -> #8e97b8 | nav-image (#0c0e1e..#161928) | 6.0588 -> 6.0588 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | bg (#0c0f1f) | 8.9237 -> 8.9237 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | card (#151a33) | 8.0244 -> 8.0244 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | card-2 (#111529) | 8.4682 -> 8.4682 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | active (#1a2040) | 7.4361 -> 7.4361 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | teal-soft (#16283a) | 7.0418 -> 7.0418 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | purple-soft (#231e3f) | 7.4115 -> 7.4115 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | danger-soft (#321e32) | 7.2032 -> 7.2032 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | body-image (#0b0e1c..#242735) | 6.9439 -> 6.9439 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | hero-image (#0b0e1c..#242735) | 1.0000 -> 6.9439 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | empty-image (#0b0e1c..#242735) | 6.9439 -> 6.9439 | 4.5
--v2-sub | #9fb0e8 -> #9fb0e8 | nav-image (#0c0e1e..#161928) | 8.2099 -> 8.2099 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | bg (#0c0f1f) | 5.2843 -> 5.9514 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | card (#151a33) | 4.7517 -> 5.3516 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | card-2 (#111529) | 5.0145 -> 5.6476 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | active (#1a2040) | 4.4033 -> 4.9593 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | teal-soft (#16283a) | 4.1698 -> 4.6963 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | purple-soft (#231e3f) | 4.3888 -> 4.9429 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | danger-soft (#321e32) | 4.2654 -> 4.8039 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | body-image (#0b0e1c..#242735) | 4.1119 -> 4.6310 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | empty-image (#0b0e1c..#242735) | 4.1119 -> 4.6310 | 4.5
--v2-faint | #7c86a8 -> hsl(226.363636,20.183486%,60.5%) | nav-image (#0c0e1e..#161928) | 4.8615 -> 5.4753 | 4.5
--v2-bg | #0c0f1f -> #0c0f1f | primary (#7884ff) | 5.3818 -> 5.9245 | 4.5
--v2-bg | #0c0f1f -> #0c0f1f | primary-hover (#8290ff) | 6.6885 -> 6.6885 | 4.5
--v2-bg | #0c0f1f -> #0c0f1f | text (#e8ebf5) | 15.9756 -> 15.9756 | 4.5
--v2-accent | #56d6e8 -> #56d6e8 | teal-soft (#16283a) | 8.7076 -> 8.7076 | 4.5
--v2-accent | #56d6e8 -> #56d6e8 | card (#151a33) | 9.9227 -> 9.9227 | 4.5
--v2-accent | #56d6e8 -> #56d6e8 | card-2 (#111529) | 10.4715 -> 10.4715 | 4.5
--v2-accent | #56d6e8 -> #56d6e8 | active (#1a2040) | 9.1952 -> 9.1952 | 4.5
--v2-danger | #ff9aaa -> #ff9aaa | danger-soft (#321e32) | 7.6457 -> 7.6457 | 4.5
--v2-purple | #b9a5ff -> #b9a5ff | purple-soft (#231e3f) | 7.4411 -> 7.4411 | 4.5
--v2-purple | #b9a5ff -> #b9a5ff | teal-soft (#16283a) | 7.0699 -> 7.0699 | 4.5
--v2-label | #a9b4d8 -> #a9b4d8 | hero-image (#0b0e1c..#242735) | 1.0000 -> 7.1959 | 4.5
--v2-number | #f2f5ff -> #f2f5ff | hero-image (#0b0e1c..#242735) | 1.1864 -> 13.5926 | 3
--v2-primary | #6e7bff -> hsl(234.620690,100%,73.5%) | card (#151a33) | 4.8395 -> 5.3275 | 4.5
--v2-primary | #6e7bff -> hsl(234.620690,100%,73.5%) | active (#1a2040) | 4.4846 -> 4.9369 | 4.5
--v2-primary-dim | #3c479a -> hsl(232.978723,43.925234%,66%) | card (#151a33) | 2.0857 -> 5.3348 | 4.5
--v2-primary-dim | #3c479a -> hsl(232.978723,43.925234%,66%) | active (#1a2040) | 1.9328 -> 4.9436 | 4.5
--v2-rank-faint | #3a4270 -> hsl(231.111111,31.764706%,63.5%) | card (#151a33) | 1.7868 -> 5.3449 | 4.5
--v2-rank-faint | #3a4270 -> hsl(231.111111,31.764706%,63.5%) | active (#1a2040) | 1.6558 -> 4.9530 | 4.5

STATE TABLE: state | foreground/background | before -> after | AA
primary:disabled | dim/card-2 (opaque) | 2.8103 -> 6.2495 | 4.5
secondary:disabled | dim/card-2 (opaque) | 3.2882 -> 6.2495 | 4.5
text-button:disabled | dim/card-2 (opaque) | 1.3740 -> 6.2495 | 4.5
form-errors | danger/danger-soft | 7.6457 -> 7.6457 | 4.5
notice | text/teal-soft | 12.6065 -> 12.6065 | 4.5
notice-link | sub/teal-soft | 7.0418 -> 7.0418 | 4.5
placeholder | dim/card-2 | 6.2495 -> 6.2495 | 4.5
placeholder-focused | dim/active | 5.4878 -> 5.4878 | 4.5
native-disabled-input/select/textarea/button | dim/card-2 | UA-dependent -> 6.2495 | 4.5
NON-TEXT TABLE: state | before -> after | AA
empty-dashed-border/bg | 1.3679 -> 5.9514 | 3
empty-dashed-border/body-image | 1.0644 -> 4.6310 | 3
empty-dashed-border/empty-image | 1.0644 -> 4.6310 | 3
focus-ring-3px/bg | 11.0347 -> 11.0347 | 3
focus-ring-3px/card | 9.9227 -> 9.9227 | 3
focus-ring-3px/card-2 | 10.4715 -> 10.4715 | 3
focus-ring-3px/active | 9.1952 -> 9.1952 | 3
focus-ring-3px/teal-soft | 8.7076 -> 8.7076 | 3
focus-ring-3px/purple-soft | 9.1648 -> 9.1648 | 3
focus-ring-3px/danger-soft | 8.9072 -> 8.9072 | 3
focus-ring-3px/body-image | 8.5865 -> 8.5865 | 3
focus-ring-3px/hero-image | 1.0000 -> 8.5865 | 3
focus-ring-3px/empty-image | 8.5865 -> 8.5865 | 3
focus-ring-3px/nav-image | 10.1520 -> 10.1520 | 3
Legacy v1 focus #7894f1/bg: 6.6181 (v1 untouched; current accent/bg: 11.0347)
FAILURE TOKENS BEFORE: 10 --v2-bg, --v2-dim, --v2-faint, --v2-label, --v2-number, --v2-primary, --v2-primary-dim, --v2-rank-faint, --v2-sub, --v2-text
FAILURES AFTER: []
MINIMUM TEXT AFTER: 4.6310:1
TEXT PAIRS: 70

X2_AUDIT_PY_BEGIN
from pathlib import Path
import colorsys
import re

path = Path("static/studio/design-system.css")
css = re.sub(r"/\*" + r".*?\*" + "/", "", path.read_text(encoding="utf-8"), flags=re.S)
tokens = dict(re.findall(r"(--v2-[\w-]+):([^;]+);", css))
old = dict(tokens)
old.update({
    "--v2-primary": "#6e7bff", "--v2-primary-dim": "#3c479a",
    "--v2-faint": "#7c86a8", "--v2-rank-faint": "#3a4270",
    "--v2-overlay-mid": "rgba(12,15,31,.78)",
    "--v2-overlay-light": "rgba(12,15,31,.34)",
    "--v2-overlay-clear": "rgba(12,15,31,.12)",
})

def color(value):
    if value.startswith("#"):
        v = value[1:]
        v = "".join(c * 2 for c in v) if len(v) == 3 else v
        return tuple(int(v[i:i+2], 16) / 255 for i in (0, 2, 4))
    parts = list(map(float, re.findall(r"[\d.]+", value)))
    if value.startswith("hsl"):
        h, s, l = parts
        return colorsys.hls_to_rgb(h / 360, l / 100, s / 100)
    return tuple(v / 255 for v in parts[:3])

def luminance(rgb):
    return sum(w * (v / 12.92 if v <= .04045 else ((v + .055) / 1.055) ** 2.4)
               for w, v in zip((.2126, .7152, .0722), rgb))

def blend(top, bottom, alpha):
    return tuple(alpha * a + (1 - alpha) * b for a, b in zip(top, bottom))

def ratio(a, b):
    lo, hi = sorted((luminance(a), luminance(b)))
    return (hi + .05) / (lo + .05)

def hex_rgb(rgb):
    return "#" + "".join(f"{round(v * 255):02x}" for v in rgb)

def surface(name, values):
    if name in ("body-image", "hero-image", "empty-image", "nav-image"):
        key = {"hero-image": "overlay-clear", "empty-image": "empty-overlay",
               "nav-image": "nav-bg"}.get(name)
        alpha = .9 if name == "body-image" else float(
            re.findall(r"[\d.]+", values["--v2-" + key])[-1])
        base = color(values["--v2-bg"])
        return blend(base, (0, 0, 0), alpha), blend(base, (1, 1, 1), alpha)
    c = color(values["--v2-" + name])
    return c, c

def contrast(fg, bounds):
    lo, hi = sorted(map(luminance, bounds))
    f = luminance(fg)
    if lo <= f <= hi:
        return 1.0
    return min(ratio(fg, b) for b in bounds)

# Image rows are conservative intervals for ANY RGB image, ANY cover crop.
# Monotonic sRGB luminance bounds every gradient between its darkest/lightest stop.
# The before floor of 1.00 is a bound, not a sampled/rendered pixel measurement.
# Avatar gradients are bounded by both endpoints. Large numbers alone use 3:1;
# all other text (including 19px/500 ranks and inactive controls) uses 4.5:1.
common = ["bg", "card", "card-2", "active", "teal-soft", "purple-soft",
          "danger-soft", "body-image", "hero-image", "empty-image", "nav-image"]
roles = {
    "text": common, "dim": common, "sub": common,
    "faint": [b for b in common if b != "hero-image"],
    "bg": ["primary", "primary-hover", "text"],
    "accent": ["teal-soft", "card", "card-2", "active"],
    "danger": ["danger-soft"],
    "purple": ["purple-soft", "teal-soft"],
    "label": ["hero-image"], "number": ["hero-image"],
    "primary": ["card", "active"], "primary-dim": ["card", "active"],
    "rank-faint": ["card", "active"],
}
uses = {}
for selector, declarations in re.findall(r"([^{}]+)\{([^{}]*)\}", css):
    for token in re.findall(r"(?:^|;)color:var\((--v2-[\w-]+)\)", declarations):
        uses.setdefault(token, []).append(selector.strip())
assert set(uses) == {"--v2-" + key for key in roles}, "Audit must cover every foreground token"
assert tokens["--v2-focus-width"] == "3px"
assert min(float(re.findall(r"[\d.]+", tokens["--v2-overlay-" + stop])[-1])
           for stop in ("strong", "mid", "light", "clear")) >= .9
assert "outline:var(--v2-focus-width) solid var(--v2-accent)" in css
for name in ("primary", "primary-dim", "faint", "rank-faint"):
    key = "--v2-" + name
    h0, l0, s0 = colorsys.rgb_to_hls(*color(old[key]))
    h1, l1, s1 = colorsys.rgb_to_hls(*color(tokens[key]))
    assert abs(h0 - h1) < 1e-7 and abs(s0 - s1) < 1e-7 and l1 > l0

blocks = {s.strip(): d for s, d in re.findall(r"([^{}]+)\{([^{}]*)\}", css)}
for selector in (".primary:disabled,.secondary:disabled,.text-button:disabled",
                 "input:not([type=checkbox]):not([type=radio]):not([type=hidden]):disabled,textarea:disabled,select:disabled,button:disabled"):
    d = blocks[selector]
    assert "opacity:1" in d and "background:var(--v2-card-2)" in d
    assert "color:var(--v2-dim)" in d
for selector in (".empty-state", ".v2-empty"):
    assert "border:1px dashed var(--v2-faint)" in blocks[selector]
assert "color:var(--v2-danger)" in blocks[".errorlist"]
assert "background:var(--v2-danger-soft)" in blocks[".errorlist"]
assert "background:var(--v2-teal-soft)" in blocks[".notice"]
assert "color:var(--v2-text)" in blocks[".notice"]
assert "opacity:1" in blocks["input::placeholder,textarea::placeholder"]

fail_before, fail_after, after_ratios = set(), [], []
print("FOREGROUND TOKEN / SELECTOR INVENTORY (inherited text uses its ancestor role)")
for token, selectors in sorted(uses.items()):
    print(token + " = " + tokens[token] + " [" + hex_rgb(color(tokens[token])) + "]")
    print("  " + "; ".join(selectors))
print("\nFULL TABLE: token | foreground before -> after | background (after bounds) | before -> after | AA")
for name, backgrounds in roles.items():
    key = "--v2-" + name
    threshold = 3.0 if name == "number" else 4.5
    for background in backgrounds:
        bounds = surface(background, tokens)
        before = contrast(color(old[key]), surface(background, old))
        after = contrast(color(tokens[key]), bounds)
        after_ratios.append(after)
        if before < threshold:
            fail_before.add(key)
        if after < threshold:
            fail_after.append((key, background, after))
        bg = "..".join(dict.fromkeys(map(hex_rgb, bounds)))
        print(f"{key} | {old[key]} -> {tokens[key]} | {background} ({bg}) | {before:.4f} -> {after:.4f} | {threshold:g}")

# Whole-element opacity composites BOTH glyph and fill with the parent.
print("\nSTATE TABLE: state | foreground/background | before -> after | AA")
for name in ("primary", "secondary", "text-button"):
    key = "--v2-bg" if name == "primary" else "--v2-sub"
    bgs = ["primary", "primary-hover"] if name == "primary" else (
        ["card-2", "active"] if name == "secondary" else [None, "active"])
    pairs = []
    for parent_name in ("bg", "card", "active", "body-image", "hero-image"):
        for parent in surface(parent_name, old):
            for bg in bgs:
                before_fg = blend(color(old[key]), parent, .65)
                before_bg = blend(color(old["--v2-" + bg]), parent, .65) if bg else parent
                pairs.append(ratio(before_fg, before_bg))
    before = min(pairs)
    after = ratio(color(tokens["--v2-dim"]), color(tokens["--v2-card-2"]))
    if before < 4.5:
        fail_before.add(key)
    after_ratios.append(after)
    assert after >= 4.5
    print(f"{name}:disabled | dim/card-2 (opaque) | {before:.4f} -> {after:.4f} | 4.5")
for name, fg, bg in (
    ("form-errors", "danger", "danger-soft"),
    ("notice", "text", "teal-soft"),
    ("notice-link", "sub", "teal-soft"),
    ("placeholder", "dim", "card-2"),
    ("placeholder-focused", "dim", "active"),
    ("native-disabled-input/select/textarea/button", "dim", "card-2"),
):
    before = ratio(color(old["--v2-" + fg]), color(old["--v2-" + bg]))
    after = ratio(color(tokens["--v2-" + fg]), color(tokens["--v2-" + bg]))
    assert after >= 4.5
    after_ratios.append(after)
    prior = "UA-dependent" if name.startswith("native-disabled") else f"{before:.4f}"
    print(f"{name} | {fg}/{bg} | {prior} -> {after:.4f} | 4.5")
print("NON-TEXT TABLE: state | before -> after | AA")
for name, backgrounds in (("empty-dashed-border", ["bg", "body-image", "empty-image"]),
                          ("focus-ring-3px", common)):
    old_fg = old["--v2-line"] if name == "empty-dashed-border" else old["--v2-accent"]
    new_fg = tokens["--v2-faint"] if name == "empty-dashed-border" else tokens["--v2-accent"]
    for bg in backgrounds:
        before = contrast(color(old_fg), surface(bg, old))
        after = contrast(color(new_fg), surface(bg, tokens))
        assert after >= 3
        print(f"{name}/{bg} | {before:.4f} -> {after:.4f} | 3")
print("Legacy v1 focus #7894f1/bg:", f'{ratio(color("#7894f1"), color(tokens["--v2-bg"])):.4f}',
      "(v1 untouched; current accent/bg:",
      f'{ratio(color(tokens["--v2-accent"]), color(tokens["--v2-bg"])):.4f})')
print("FAILURE TOKENS BEFORE:", len(fail_before), ", ".join(sorted(fail_before)))
print("FAILURES AFTER:", fail_after)
print("MINIMUM TEXT AFTER:", f"{min(after_ratios):.4f}:1")
print("TEXT PAIRS:", sum(map(len, roles.values())) + 9)
assert not fail_after, fail_after
X2_AUDIT_PY_END
*/
