@charset "UTF-8";
:root{--bg:#f8fafc;--ink:#142235;--muted:#536378;--line:#cad3df;--accent:#08756c;--surface:#edf2f7;--button:#142235;--button-ink:#fff;--font:"Segoe UI",Calibri,sans-serif;color-scheme:light;
  /* S2-019 motion vocabulary: one curve, three durations, one small amplitude. Precision over
     amplitude -- decisive settling, never a bounce or an overshoot. */
  --ease:cubic-bezier(.2,.7,.3,1);--dur-fast:160ms;--dur-normal:280ms;--dur-signature:600ms;--amp:6px}
:root[data-theme=dark]{--bg:#111b28;--ink:#edf3fa;--muted:#a6b5c7;--line:#394859;--accent:#61d5bd;--surface:#1a2939;--button:#b4f1df;--button-ink:#112b27;color-scheme:dark}
*{box-sizing:border-box}html{scroll-behavior:smooth;scroll-padding-top:28px}body{margin:0;background:var(--bg);color:var(--ink);font:17px/1.65 var(--font)}a{color:inherit;text-underline-offset:5px}a:hover{color:var(--accent)}button{font:inherit;cursor:pointer}button[hidden]{display:none}a:focus-visible,button:focus-visible{outline:3px solid var(--accent);outline-offset:5px}.skip{position:absolute;top:-100px;left:16px;padding:10px;background:var(--bg);z-index:5}.skip:focus{top:12px}.topbar,main{max-width:1240px;margin:auto;padding-left:5%;padding-right:5%}.topbar{display:flex;align-items:center;gap:22px;min-height:100px;border-bottom:1px solid var(--line)}.wordmark{font-size:36px;font-weight:800;text-decoration:none;letter-spacing:-2px}.mark-dot{color:var(--accent)}.edition,.eyebrow,.date{font:13px/1.6 Consolas,"Courier New",monospace;letter-spacing:1.2px}.edition{margin-right:auto;color:var(--muted)}.quiet{position:relative;display:inline-flex;align-items:center;gap:5px;border:1px solid var(--line);background:transparent;color:var(--muted);padding:6px 10px;border-radius:3px;font-size:11px;white-space:nowrap;flex-shrink:0}.quiet::before{content:"";position:absolute;top:50%;left:50%;width:44px;height:44px;transform:translate(-50%,-50%)}html[lang="en"] #lang{font-size:10px}html[lang="ja"] #lang{font-size:13px}
/* S2-020: theme-bulb icon (inline SVG, no emoji -- inherits currentColor). Rays retract in dark
   mode at the fast tier so it reads as part of the button, not its own event. The accepted
   composite: a pull cord leads, a bloom starts as the cord bottoms out and hands off into the
   existing 450ms circular reveal below -- one behavior, no alternate to select. */
.bulb-wrap{position:relative;display:inline-flex;flex-shrink:0}
.theme-bulb{flex-shrink:0;overflow:visible;display:block}
.bulb-rays{transform-origin:12px 12px;transition:transform var(--dur-fast) var(--ease),opacity var(--dur-fast) var(--ease)}
:root[data-theme=dark] .bulb-rays{transform:scale(.4);opacity:0}
:root[data-theme=dark] .bulb-cord{opacity:.5}
.bulb-cord-string,.bulb-cord-knob{transform-origin:12px 23px;transition:transform 130ms var(--ease)}
.bulb-cord.pulled .bulb-cord-string{transform:scaleY(1.35)}
.bulb-cord.pulled .bulb-cord-knob{transform:translateY(3px)}
.bulb-wrap::after{content:"";position:absolute;inset:-6px;border-radius:50%;background:radial-gradient(circle,var(--accent) 0%,transparent 70%);opacity:0;pointer-events:none}
.bulb-wrap.bloom-play::after{animation:bulb-bloom-quiet 450ms var(--ease) forwards}
@keyframes bulb-bloom-quiet{0%{opacity:0;transform:scale(.3)}30%{opacity:.55;transform:scale(1)}100%{opacity:0;transform:scale(2.2)}}
/* OPUS-EGG: the toggle is a light switch, so flicking it too many times burns the bulb out.
   The bulb goes black and the page flickers with it. Three flashes across ~1.1s, uneven, which
   sits under the published photosensitivity threshold; a dying filament does not strobe evenly.
   Scoped through #theme and marked important because the button's own colour rule and the
   page-wide reduced-motion reset both sit downstream of this and would otherwise win.
   Suppressed entirely under reduced-motion along with everything else on this page. */
#theme .theme-bulb{transition:opacity 90ms linear,color 90ms linear}
#theme .theme-bulb.burnt{color:#05070a!important;opacity:.22!important}
/* dark mode: a near-black bulb on a near-black ground just disappears, which reads as a
   missing icon rather than a dead one. Dim it instead so the shape survives. */
:root[data-theme=dark] #theme .theme-bulb.burnt{color:inherit!important;opacity:.3!important}
#theme .theme-bulb.burnt .bulb-rays{opacity:0!important;transform:scale(.4)!important}
#theme .theme-bulb.burnt .bulb-cord{opacity:.15!important}
@media(prefers-reduced-motion:reduce){#theme .theme-bulb,#theme .theme-bulb.burnt{transition:none!important;color:inherit!important;opacity:1!important}}
.hero{display:grid;grid-template-columns:1fr 1fr;gap:8%;padding:70px 0 55px}.eyebrow{color:var(--accent);font-weight:700;margin:0 0 22px}h1,h2,h3,p{margin-top:0}h1{font-size:clamp(64px,7.2vw,96px);font-weight:650;letter-spacing:-5px;line-height:.98;margin-bottom:30px}.lead{font-size:30px;line-height:1.25;letter-spacing:-.7px;margin-bottom:24px}.location{font-size:14px;color:var(--muted);margin:0}.location span{margin:0 8px}.location .unbreak{white-space:nowrap;margin:0}.hero-detail{padding-top:46px}.summary{font-size:22px;line-height:1.5}.hero-detail>p+ p{color:var(--muted)}.actions{display:grid;gap:10px;margin-top:30px}.primary,.secondary{display:flex;align-items:center;justify-content:space-between;padding:15px 19px;text-decoration:none;border:1px solid var(--line);font-weight:600}.primary{background:var(--button);color:var(--button-ink);border-color:var(--button)}.primary:hover{color:var(--button-ink);filter:brightness(1.2)}.primary span{font:14px Consolas,monospace}.contact-links{display:flex;gap:26px;margin-top:22px;font-size:15px}.metrics{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--line);border-bottom:1px solid var(--line);padding:28px 0}.metrics>div{padding:0 24px;border-left:1px solid var(--line)}.metrics>div:first-child{padding-left:0;border:0}.metrics strong{font-size:48px;font-weight:550;line-height:1.2;letter-spacing:-2px}.metrics strong span{font-size:27px;color:var(--accent)}.metrics p{font-size:14px;line-height:1.5;margin:10px 0 0}.metrics p span{color:var(--muted)}.sections{display:flex;flex-wrap:wrap;gap:25px;padding:22px 0;font-size:14px}.sections a{text-decoration:none}.section{display:grid;grid-template-columns:30% 1fr;gap:6%;padding:55px 0;border-bottom:1px solid var(--line)}h2{font-size:34px;line-height:1.2;letter-spacing:-1px;font-weight:550}h3{font-size:21px;line-height:1.4;margin-bottom:6px}.role{margin-bottom:38px}.role:last-child{margin:0}.role-title{display:flex;gap:15px;justify-content:space-between}.position{font-size:16px;line-height:1.5;margin-bottom:5px;max-width:450px}.muted{color:var(--muted)}.small{font-size:14px;font-weight:400}.date{font-size:12px;letter-spacing:.3px;flex-shrink:0;padding-top:4px;color:var(--muted)}.role-title .muted{font-size:14px;margin-bottom:0}.role ul{padding-left:19px;margin:17px 0 0}.role li{padding-left:4px;font-size:16px;line-height:1.65;margin-bottom:9px}.expertise{display:grid;gap:28px}.expertise p,.education p{font-size:16px;margin-bottom:5px}.technical{border-left:3px solid var(--accent);padding-left:24px}.tags{display:flex;gap:9px;flex-wrap:wrap;margin-top:17px}.tags span{padding:5px 10px;background:var(--surface);font:14px/1.5 Consolas,monospace}.education{display:grid;gap:26px}.download-section{display:grid;grid-template-columns:1fr 1fr;gap:8%;margin:65px 0 20px;padding:35px;background:var(--surface)}.download-section h2{font-size:36px}.download-section>div>p:last-child{font-size:16px;color:var(--muted)}.download-options{display:grid;gap:0}.download-options>a,.download-options>button{display:flex;flex-direction:column;text-align:left;align-items:flex-start;background:transparent;color:var(--ink);border:0;border-bottom:1px solid var(--line);padding:14px 0;text-decoration:none}.download-options strong{font-size:17px;font-weight:600}.download-options span{font-size:14px;color:var(--muted)}.download-options a:hover strong,.download-options button:hover strong{color:var(--accent)}footer{padding:40px 0 30px}footer>a{font-size:20px}footer>div{display:flex;gap:10px;margin-top:18px}#status{min-height:26px;font-size:14px;margin:8px 0}footer>.muted{border-top:1px solid var(--line);padding-top:20px;margin:10px 0 0}
@media(max-width:850px){.hero{gap:5%}h1{font-size:76px}.role-title{display:block}.date{margin-top:8px}.section{grid-template-columns:26% 1fr;gap:5%}.download-section{padding:25px;gap:5%}}
@media(max-width:620px){body{font-size:16px}.topbar{min-height:80px;gap:12px}.edition{font-size:10px;letter-spacing:.5px}.hero{grid-template-columns:1fr;padding:38px 0}.hero-detail{padding-top:18px}h1{font-size:74px}.lead{font-size:27px}.summary{font-size:20px}.metrics{padding:20px 0}.metrics>div{padding:0 12px}.metrics strong{font-size:31px;letter-spacing:-1.5px}.metrics strong span{font-size:20px}.metrics p{font-size:12px}.sections{gap:12px 20px;font-size:13px}.section{grid-template-columns:1fr;gap:16px;padding:35px 0}.eyebrow{margin-bottom:15px}.section-heading h2 br{display:none}h2{font-size:30px}.download-section{grid-template-columns:1fr;margin-top:35px;padding:24px}.download-section h2{font-size:32px}footer>a{font-size:17px}}
/* S2-014: at very narrow widths, the topbar now needs room for two .quiet buttons (language + theme) where
   it previously only carried one -- measured overflow at 390px (topbar needed 424px) with both buttons
   present using white-space:nowrap (added so "日本語" stops breaking mid-character across three lines, a
   real bug CJK text is prone to that plain English text isn't). The decorative edition label is the one
   piece here that is not information the visitor needs, so it is what gives way first. This is its own
   rule, deliberately not merged into the 620px block above, so it only fires at the width that actually
   needs it. */
@media(max-width:400px){.edition{display:none}}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,*::before,*::after{transition:none!important;animation:none!important}::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}}
@media print{ :root,:root[data-theme=dark]{--bg:#fff;--ink:#000;--muted:#333;--line:#aaa;--accent:#000;--surface:#fff;color-scheme:light}body{font:10pt/1.4 Arial,sans-serif}.topbar,.actions,.sections,.contact-links,.download-section,footer>div,#status,.skip{display:none}main{max-width:none;padding:0}.hero{display:block;padding:0 0 15px}h1{font-size:30pt;line-height:1;letter-spacing:-1px;margin:0 0 10px}h1 br{display:none}h1 br:after{content:' '}.lead{display:none}.hero .eyebrow{margin-bottom:6px;font-size:9pt}.hero-detail{padding:12px 0 0}.summary{font-size:11pt;margin-bottom:5px}.hero-detail>p+p{font-size:10pt;margin:0}.location{font-size:9pt}.metrics{padding:12px 0}.metrics strong{font-size:24pt}.metrics p{font-size:8pt}.section{display:block;padding:18px 0}.section-heading .eyebrow{display:none}.section-heading h2{font-size:15pt;margin-bottom:12px}.section-heading h2 br{display:none}.role{margin-bottom:17px;break-inside:avoid}.role-title{display:flex}h3{font-size:12pt}.position,.role li,.expertise p,.education p{font-size:10pt}.role ul{margin-top:8px}.role li{margin-bottom:4px}.expertise,.education{gap:12px}.technical{padding-left:12px}.tags span{font-size:9pt;padding:2px 4px}.role-title .muted,.date,.small{font-size:8pt}footer{padding:15px 0}footer>a{font-size:10pt}a{text-decoration:none}h2,h3{break-after:avoid}@page{margin:15mm}}

.mobile-download{display:none}
@media(max-width:620px){body{padding-bottom:calc(84px + env(safe-area-inset-bottom))}.mobile-download{display:block;position:fixed;bottom:0;left:0;right:0;padding:12px 5% calc(12px + env(safe-area-inset-bottom));background:var(--bg);border-top:1px solid var(--line);z-index:3}.mobile-download.hero-visible{display:none}.mobile-download a{display:flex;justify-content:space-between;padding:13px 18px;min-height:48px;background:var(--button);color:var(--button-ink);text-decoration:none;font-weight:600}.sections a{padding:10px 0;min-height:44px}.contact-links a{padding:10px 0;min-height:44px}.metrics p{font-size:14px}.edition{font-size:12px}.metrics{grid-template-columns:1fr;gap:16px}.metrics>div,.metrics>div:first-child{padding:0;border:0;display:flex;align-items:center;gap:20px}.metrics strong{min-width:112px;font-size:36px}.metrics p{margin:0}.primary,.secondary{min-height:52px}}
@media print{body{padding-bottom:0}.mobile-download{display:none!important}}

.connect-section{border-top:1px solid var(--line);padding:clamp(2rem,5vw,3.25rem) 0;display:grid;gap:1.25rem}
.connect-card{display:flex;justify-content:center;align-items:center;padding:clamp(1rem,4vw,1.75rem);border:1px solid var(--line);border-radius:14px;background:var(--surface)}
.connect-card .badge-base__link{color:var(--accent);font-weight:600;text-decoration:none}
@media print{.connect-section{display:none}}

.li-card{display:grid;gap:.5rem;width:min(100%,22rem);padding:1.25rem 1.35rem;border:1px solid var(--line);border-radius:12px;background:var(--bg);text-decoration:none;color:var(--ink);transition:border-color .18s ease,transform .18s ease}
.li-card:hover,.li-card:focus-visible{border-color:var(--accent);transform:translateY(-2px)}
.li-card-top{display:flex;align-items:center;gap:.5rem}
.li-mark{width:1.15rem;height:1.15rem;fill:var(--accent);flex:none}
.li-word{font-size:.78rem;letter-spacing:.09em;text-transform:uppercase;color:var(--muted);font-weight:600}
.li-name{font-size:1.35rem;font-weight:700;line-height:1.15}
.li-headline{color:var(--muted);font-size:.95rem;line-height:1.45}
.li-affil{color:var(--muted);font-size:.85rem}
.li-cta{margin-top:.35rem;font-weight:600;color:var(--accent)}
@media(max-width:520px){.li-card{width:100%;padding:1.1rem}.li-name{font-size:1.2rem}}

/* S2-019 motion pass. Every element these classes touch is already fully visible (opacity:1,
   transform:none) in its own base rule elsewhere in this file. JS only ever ADDS one of these
   classes to run a short settle animation FROM a barely-offset, still-legible state TO that same
   rest state -- never the reverse, and nothing here is ever hidden while waiting for JS. If a
   script is slow, blocked, or throws, the page already looks exactly like the finished state. */
.reveal-up{animation:reveal-up var(--dur-normal) var(--ease) both}
@keyframes reveal-up{from{transform:translateY(var(--amp));opacity:.85}to{transform:translateY(0);opacity:1}}

/* Hero signature moment, both languages, on the accent dot after "Bryant." only (not the wordmark's
   own dot -- scoped to #name in app.js). Three text-surface treatments were tried and cut; the dot
   is the brand device already, a single small element, script-independent since the name stays
   Latin on both pages. Rest state is already opacity:1/no transform via .mark-dot's own rule
   elsewhere in this file, so this is visible-baseline by default with or without JS. */
.dot-settle{animation:dot-settle 380ms var(--ease) both}
@keyframes dot-settle{from{opacity:.5;transform:scale(.6)}to{opacity:1;transform:scale(1)}}

.nav-progress{position:relative;height:2px;background:var(--line);margin-top:-1px}
.nav-progress>span{display:block;height:100%;width:0%;background:var(--accent)}
.sections a{transition:color var(--dur-fast) var(--ease)}
.sections a.active{color:var(--accent)}

#experience .section-body{position:relative}
.rail{position:absolute;left:-18px;top:6px;bottom:6px;width:2px;background:var(--line)}
.rail>span{display:block;width:100%;height:0%;background:var(--accent);transform-origin:top}

.metric-rule{display:block;height:2px;margin-top:10px;background:var(--accent);transform:scaleX(0);transform-origin:left}
.metric-rule.fill{animation:metric-rule var(--dur-normal) var(--ease) both}
@keyframes metric-rule{from{transform:scaleX(0)}to{transform:scaleX(1)}}

.status-in{animation:status-in var(--dur-fast) var(--ease) both}
@keyframes status-in{from{transform:translateY(4px);opacity:.7}to{transform:translateY(0);opacity:1}}

.li-cta-pass{position:relative}
.li-cta-pass::after{content:"";position:absolute;inset:-2px -6px;background:var(--accent);opacity:0;transform:scaleX(0);transform-origin:left;pointer-events:none}
.li-cta-pass.play::after{animation:li-pass var(--dur-signature) var(--ease) forwards}
@keyframes li-pass{0%{opacity:.28;transform:scaleX(0)}70%{opacity:.28;transform:scaleX(1)}100%{opacity:0;transform:scaleX(1)}}

/* theme toggle circular reveal (View Transitions): the browser's own default cross-fade is
   suppressed so only our clip-path circle, driven from app.js, is visible */
::view-transition-old(root),::view-transition-new(root){animation:none;mix-blend-mode:normal}

/* expertise emphasis: CSS-only, both hover and focus so keyboard use gets the same cue; never
   hides anything, since touch has no hover and must see the full content regardless */
.expertise>div{transition:color var(--dur-fast) var(--ease)}
.expertise>div:hover h3,.expertise>div:focus-within h3{color:var(--accent)}
