/* Brass app pages. Built on brass.css tokens: paper, ink, fine rules, Caslon for words, Fragment Mono for numbers. */
.app{background:var(--paper);color:var(--ink)}
.app .top{height:auto;min-height:72px;background:var(--paper);display:flex;align-items:center}
.app .top .wrap{gap:16px;flex-wrap:wrap;padding-block:14px}
.app .top nav{display:flex;flex-wrap:wrap;gap:8px 28px;align-items:center}
.app .top nav a[aria-current=page],.app .top nav a[aria-current=true]{text-decoration:underline;text-underline-offset:6px;text-decoration-thickness:1px}
.app .top form{margin:0}
.app .top .balance-chip{font:400 14px var(--mono);color:var(--soft);text-decoration:none;border:1px solid var(--rule);border-radius:999px;padding:6px 12px}
.app .top .balance-chip:hover{color:var(--ink);border-color:var(--strong)}
.app .link-button{font:inherit;font-size:var(--nav);background:transparent;color:var(--ink);padding:0;min-height:32px;border:0;cursor:pointer;text-decoration:none}
.app .link-button:hover{text-decoration:underline}
.app-main{padding-block:64px 112px;min-height:62vh}
.app-main.narrow{max-width:560px}
.app-main.medium{max-width:880px}
.app-main.wide{max-width:1240px}
.app h1{font:400 clamp(36px,4.6vw,var(--h2))/1.08 var(--display);letter-spacing:-.01em;margin:0 0 20px;text-wrap:balance;max-width:22ch}
.app h2{font:400 clamp(24px,2.6vw,30px)/1.15 var(--display);margin:56px 0 16px;text-wrap:balance}
.app h3{font:700 var(--body)/1.3 var(--text);margin:0 0 6px}
/* An admin subheading after a table or a list of totals gets room above it (visual, round 10). */
.app .table-scroll+h3,.app dl.totals+h3,.app table.admin+h3{margin-top:28px}
.app .table-scroll+p,.app table.admin+p{margin-top:16px}
.app #delete-chat{padding-inline:0}
.app p{font-size:var(--body);line-height:1.6;max-width:62ch;margin:0 0 16px}
.app .lede{font-size:var(--lede);line-height:1.5;color:var(--ink)}
.app .lot{margin-bottom:14px}
.app a{color:var(--ink);text-underline-offset:3px;text-decoration-thickness:1px}
.app .soft{color:var(--soft)}
.app .mono{font-family:var(--mono)}
.app .fine-print{font-size:var(--small);color:var(--soft);line-height:1.55;margin-top:20px}
.app :focus-visible{outline:2px solid var(--brass-ink);outline-offset:3px}

/* Buttons */
.app button,.app .button{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:52px;padding:0 26px;background:var(--ink);color:var(--paper-light);border:1px solid var(--ink);border-radius:2px;font:400 15px var(--mono);text-decoration:none;cursor:pointer;transition:background-color .15s ease,color .15s ease}
.app button:hover,.app .button:hover{background:#2E2A23}
.app button:active,.app .button:active{background:#000}
.app button:disabled{opacity:.55;cursor:not-allowed}
.app .button.secondary,.app button.secondary{background:transparent;color:var(--ink);border-color:var(--strong)}
.app .button.secondary:hover,.app button.secondary:hover{background:var(--paper-light);border-color:var(--ink)}
.app button.quiet,.app .button.quiet{background:transparent;color:var(--ink);border-color:transparent;padding:0 4px;min-height:40px;text-decoration:underline;text-underline-offset:4px;font-family:var(--text);font-size:var(--small)}
.app button.quiet:hover,.app .button.quiet:hover{background:transparent;color:var(--soft)}
.app .actions{display:flex;flex-wrap:wrap;gap:12px 16px;align-items:center;margin:24px 0 0}
.app .actions form{margin:0}

/* Forms */
.app form{max-width:640px}
.app fieldset{border:0;margin:0 0 32px;padding:0;min-width:0}
.app legend{font:400 12px/1.4 var(--mono);text-transform:uppercase;letter-spacing:.08em;color:var(--brass-ink);padding:0;margin-bottom:14px}
.app legend.sr-only{text-transform:none;letter-spacing:normal}
.app .field{display:block;margin-bottom:20px}
.app .field>span:first-child{display:block;font-size:var(--small);margin-bottom:8px}
.app .field .hint{display:block;font-size:14px;color:var(--soft);margin-top:6px}
.app input:not([type=radio]):not([type=checkbox]):not([type=hidden]),.app textarea,.app select{width:100%;min-width:0;min-height:50px;padding:12px 14px;border:1px solid var(--strong);border-radius:2px;background:var(--paper-light);color:var(--ink);font:400 var(--body)/1.4 var(--text)}
.app input:hover,.app textarea:hover,.app select:hover{border-color:var(--soft)}
.app input:focus,.app textarea:focus,.app select:focus{border-color:var(--ink);outline:2px solid var(--brass-ink);outline-offset:1px}
.app input[aria-invalid=true]{border-color:var(--error)}
.app textarea{resize:vertical;min-height:120px}
.app .choice{display:flex;align-items:flex-start;gap:12px;margin-bottom:14px;font-size:var(--small);line-height:1.5;cursor:pointer}
.app .choice input{margin:3px 0 0;width:18px;height:18px;accent-color:var(--ink);flex:none}
.app .two{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}
/* A check's name and address in parts: city, state and ZIP share a row, and stack on a phone (product, round 21). */
.app .check-address{max-width:34em}
.app .check-address .three{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1.5fr) minmax(0,1fr);gap:0 16px}
.app .check-address select{width:100%}
/* An address shown back as given, one part to a line, in the receipts' type (visual, round 21). */
.app .address-shown{font:400 15px/1.6 var(--mono);color:var(--ink);margin:8px 0 16px;padding:10px 16px;border-left:2px solid var(--rule)}
.app details.change-address{margin:8px 0 0}
.app details.change-address>summary{cursor:pointer;list-style:none;padding:3px 0;color:var(--ink);text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:4px;width:max-content}
.app details.change-address>summary::-webkit-details-marker{display:none}
.app details.change-address[open]>summary{margin-bottom:12px}
.app fieldset.check-fieldset{border:0;padding:0;margin:0;min-width:0}
.app .feedback{padding:16px 18px;border:1px solid var(--rule);background:var(--paper-light);max-width:none;margin:0 0 28px}
.app .feedback.error{border-color:#D9B7AE;color:var(--error)}
.app .feedback p:last-child{margin-bottom:0}

/* Buy: five coins, smallest to largest */
.app .packs{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;margin:0}
.app .pack-coin{position:relative;display:grid;justify-items:center;align-content:end;gap:10px;padding:18px 8px 16px;border:1px solid var(--rule);border-radius:2px;background:transparent;cursor:pointer;text-align:center;transition:border-color .15s ease,background-color .15s ease}
.app .pack-coin:hover{border-color:var(--strong);background:var(--paper-light)}
.app .pack-coin input{position:absolute;opacity:0;pointer-events:none}
.app .pack-coin img{width:var(--coin,64px);height:var(--coin,64px);object-fit:contain;filter:saturate(.95)}
.app .pack-coin .face{font:400 30px/1 var(--display)}
.app .pack-coin .tokens{font:400 11px/1.4 var(--mono);color:var(--soft)}
.app .pack-coin:has(input:checked){border-color:var(--ink);background:var(--paper-light);box-shadow:inset 0 0 0 1px var(--ink)}
.app .pack-coin:has(input:focus-visible){outline:2px solid var(--brass-ink);outline-offset:3px}
.app .segmented{display:inline-grid;grid-auto-flow:column;border:1px solid var(--strong);border-radius:2px;overflow:hidden}
.app .segmented label{display:block;cursor:pointer}
.app .segmented input{position:absolute;opacity:0;pointer-events:none}
.app .segmented span{display:block;padding:12px 22px;font:400 15px var(--mono);min-width:120px;text-align:center}
.app .segmented label+label span{border-left:1px solid var(--strong)}
.app .segmented input:checked+span{background:var(--ink);color:var(--paper-light)}
.app .segmented input:focus-visible+span{outline:2px solid var(--brass-ink);outline-offset:-4px}
.app .gift-fields{border-top:1px solid var(--rule);padding-top:24px;margin-top:-8px}
.app .summary{display:flex;justify-content:space-between;gap:16px;align-items:baseline;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:16px 0;margin:8px 0 20px;font-family:var(--mono);font-size:15px}
.app .summary strong{font-weight:400;font-size:20px}

/* The single token on success, claim and opened views */
.app .stage-split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:48px;align-items:center}
.app .stage-split .solo{width:min(460px,100%)}
.app .copy-link{display:flex;gap:10px;flex-wrap:wrap;margin:8px 0 12px}
.app .copy-link input{flex:1 1 260px;font-family:var(--mono)!important;font-size:14px!important}
.app .serial{font:400 13px/1.5 var(--mono);letter-spacing:.06em;color:var(--soft);text-transform:uppercase;margin:0 0 18px}
.app .starter{list-style:none;padding:0;margin:20px 0 0;display:grid;gap:10px}
.app .starter a{display:block;padding:14px 16px;border:1px solid var(--rule);border-radius:2px;text-decoration:none;font-size:var(--small);line-height:1.45;background:var(--paper-light)}
.app .starter a:hover{border-color:var(--ink)}
.app .reveal{animation:brass-in .5s ease both}
@keyframes brass-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.app .reveal{animation:brass-fade .4s ease both}@keyframes brass-fade{from{opacity:0}to{opacity:1}}}

/* Stash */
/* The balance never breaks: at 200% zoom the actions wrap under it instead (accessibility, round 9). */
.app .stash-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:flex-end;gap:24px;border-bottom:1px solid var(--rule);padding-bottom:28px}
.app .stash-head>div:first-child{flex:1 1 360px}
.app p.balance{overflow-wrap:normal;white-space:nowrap}
.app .balance{font:400 clamp(56px,8vw,96px)/1 var(--display);letter-spacing:-.02em;margin:0 0 10px}
.app .balance-sub{font:400 13px/1.5 var(--mono);color:var(--soft);margin:0}
/* The check Brass owes, in ink under the balance, with its link to the form below (visual, round 20). */
.app .balance-sub.owed-sub{color:var(--ink);margin-top:6px}
.app .pack-grid{list-style:none;padding:0;margin:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:16px}
.app .pack-card{display:grid;grid-template-rows:auto 1fr auto;border:1px solid var(--rule);border-radius:2px;background:var(--paper-light);overflow:hidden}
.app .pack-card .env{display:grid;place-items:center;background:var(--paper);border-bottom:1px solid var(--rule);aspect-ratio:16/10}
.app .pack-card .env img{width:62%;height:auto}
.app .pack-card .meta{padding:16px 16px 4px}
.app .pack-card .meta .face{font:400 30px/1 var(--display);margin:0 0 6px;color:var(--ink)}
.app .pack-card .meta p{font-size:14px;line-height:1.45;margin:0 0 4px;color:var(--soft)}
.app .pack-card .meta .state{font:400 11px/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--brass-ink);margin:0 0 8px}
.app .pack-card .card-actions{display:flex;flex-wrap:wrap;gap:8px;padding:12px 16px 16px}
.app .pack-card .card-actions form{margin:0}
.app .pack-card .card-actions button,.app .pack-card .card-actions .button{min-height:40px;padding:0 14px;font-size:13px}
.app .pack-card .card-actions .quiet{padding:0;font-size:15px}
.app .receipts{width:100%;border-collapse:collapse;font:400 13px/1.5 var(--mono)}
.app .receipts th,.app .receipts td{padding:10px 14px 10px 0;border-bottom:1px solid var(--rule);text-align:left;vertical-align:top}
/* Figures wrap rather than hide at 200% zoom; a dollar amount stays whole. */
.app .receipts th{font-weight:400;color:var(--soft);font-size:11px;letter-spacing:.06em;text-transform:uppercase}
.app .receipts td.num,.app .receipts th.num{text-align:right;padding-right:28px}
/* Amounts line up on the point at every precision from 640 px up; phones' stacked rows stay as they are (visual, round 18). */
@media (min-width:640px){.app .amount{white-space:nowrap}.app .amount.pad-2{padding-right:2ch}.app .amount.pad-4{padding-right:4ch}}
.app #size-moved:empty{margin:0}
.app .opened-list{list-style:none;padding:0;margin:0 0 40px;border-top:1px solid var(--rule)}
.app .opened-list li{display:grid;grid-template-columns:minmax(110px,auto) minmax(0,1fr) auto;column-gap:24px;align-items:baseline;padding:12px 0;border-bottom:1px solid var(--rule);font-size:var(--small)}
@media (max-width:599px){.app .opened-list li{grid-template-columns:1fr;row-gap:2px}}
.app .table-scroll{overflow-x:auto;max-width:100%;position:relative}
/* The price index's two tables share one set of column widths, so their columns line up (visual, round 13). */
/* Wide enough, and the date free to wrap at its hyphens, that WCAG text spacing never runs a date into a model name
   (accessibility, round 14). */
table.index-table{table-layout:fixed;width:100%;min-width:760px}
.guide table.index-table td.date{white-space:normal}
/* The date column holds a whole date at 16 px type (1440 px and wider) and still wraps at its hyphens under text spacing (visual, round 15). */
table.index-table .c-date{width:15%}table.index-table .c-model{width:33%}table.index-table .c-num{width:11%}table.index-table .c-sources{width:19%}
.app .banner{display:grid;grid-template-columns:120px minmax(0,1fr);gap:24px;align-items:center;border:1px solid var(--rule);background:var(--paper-light);padding:16px 20px;margin:0 0 32px}
.app .banner img{width:120px;height:auto}
.app .banner p{margin:0 0 6px}

/* Chat */
.app.chat-page .app-main{padding-block:0;min-height:0}
.app .chat-shell{display:grid;grid-template-columns:260px minmax(0,1fr);min-height:calc(100dvh - 73px)}
.app .chat-side{border-right:1px solid var(--rule);padding:20px 16px;display:flex;flex-direction:column;gap:16px;min-width:0}
.app .chat-side .new{width:100%;min-height:44px;font-size:14px}
.app .convos{list-style:none;margin:0;padding:0;display:grid;gap:2px;overflow:auto;max-height:calc(100dvh - 260px)}
.app .convos button{width:100%;justify-content:flex-start;text-align:left;min-height:40px;padding:8px 10px;background:transparent;color:var(--ink);border:0;border-radius:2px;font:400 14px/1.35 var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
.app .convos button:hover{background:var(--paper-light)}
.app .convos button:focus-visible{outline-offset:-3px}
.app .convos button[aria-current=true]{background:var(--paper-light);box-shadow:inset 2px 0 0 var(--ink)}
.app .chat-side .side-foot{margin-top:auto;font:400 12px/1.5 var(--mono);color:var(--soft)}
.app .chat-main{display:grid;grid-template-rows:auto minmax(0,1fr) auto;min-width:0}
.app .chat-bar{display:flex;flex-wrap:wrap;gap:12px 20px;align-items:center;justify-content:space-between;padding:14px 28px;border-bottom:1px solid var(--rule)}
.app .chat-bar .field{margin:0;display:flex;gap:10px;align-items:center}
.app .chat-bar .field>span:first-child{margin:0;font:400 12px var(--mono);text-transform:uppercase;letter-spacing:.08em;color:var(--brass-ink)}
.app .chat-bar select{width:auto;min-height:42px;padding:8px 12px;font-size:15px;max-width:min(440px,60vw)}
.app .chat-bar .left{font:400 15px var(--mono)}
.app .model-pick{display:grid;grid-template-columns:auto minmax(0,auto);column-gap:10px;row-gap:2px;align-items:center;min-width:0}
.app .model-pick label{margin:0;font:400 12px var(--mono);text-transform:uppercase;letter-spacing:.08em;color:var(--brass-ink)}
.app .model-pick select{grid-column:2}
.app .model-pick .model-rate{grid-column:2;margin:0;font:400 12px/1.4 var(--mono);color:var(--soft)}
.app #conversation{position:relative;overflow-y:auto;padding:28px 28px 8px;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){.app #conversation{scroll-behavior:auto}}
.app .thread{max-width:760px;margin:0 auto}
.app .chat-message{padding:0 0 28px;scroll-margin-top:20px}
/* 36 px above the 988 line when it is brought back, so the chat bar's label above it stays whole at 683x384 (visual, round 20). */
.app .chat-message .crisis{scroll-margin-top:36px;scroll-margin-bottom:12px}
.app .chat-message.user .message-body{background:var(--paper-light);border:1px solid var(--rule);border-radius:2px;padding:14px 16px;margin-left:auto;max-width:85%;white-space:pre-wrap;overflow-wrap:anywhere}
.app .chat-message.user{display:flex;justify-content:flex-end;flex-wrap:wrap}
/* "No answer, and nothing was charged." sits under the message it belongs to, at its right edge. */
.app .chat-message.user .message-receipt{flex-basis:100%;max-width:85%;margin-left:auto;text-align:right;text-wrap:balance;margin-top:8px}
.app .chat-message .who{font:400 11px var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--brass-ink);margin:0 0 10px}
/* break-word keeps a word whole in a table column; long links and strings still wrap. */
.app .chat-message.assistant .message-body{font-size:var(--body);line-height:1.65;overflow-wrap:break-word}
.app .message-body>*:first-child{margin-top:0}
.app .message-body p,.app .message-body li{max-width:none;font-size:var(--body);line-height:1.65}
.app .message-body h1,.app .message-body h2,.app .message-body h3{font:700 var(--body)/1.35 var(--text);margin:24px 0 8px}
.app .message-body ul,.app .message-body ol{padding-left:1.3em;margin:0 0 16px}
.app .message-body code{font:400 .88em var(--mono);background:var(--paper-light);border:1px solid var(--rule);border-radius:2px;padding:1px 4px}
.app .message-body pre{position:relative;background:#1E1B16;color:#EDE8DC;border-radius:2px;padding:16px 18px;margin:0 0 18px;overflow:auto;font:400 13px/1.6 var(--mono)}
.app .message-body pre code{background:none;border:0;padding:0;color:inherit;font-size:inherit}
.app .message-body pre .copy{position:absolute;top:8px;right:8px;min-height:30px;padding:0 10px;font-size:12px;background:#2E2A23;border-color:#4A443A;color:#EDE8DC}
.app .message-body table{border-collapse:collapse;margin:0 0 18px;font-size:var(--small)}
.app .message-body th,.app .message-body td{border-bottom:1px solid var(--rule);padding:8px 12px 8px 0;text-align:left}
.app .message-body blockquote{margin:0 0 16px;padding-left:16px;border-left:2px solid var(--rule);color:var(--soft)}
.app .message-body a{word-break:break-word}
.app .message-receipt,.app .message-note{font:400 12px/1.6 var(--mono);color:var(--soft);margin:12px 0 0;overflow-wrap:anywhere;max-width:none}
.app .message-note+.message-receipt{margin-top:4px}
/* Brief thinking's line under the price, at every width; it wraps rather than clip (visual and product, round 17). */
.app .model-rate .brief-note{display:block;white-space:normal}
.app .model-rate .brief-note[hidden]{display:none}
.app .typing{display:inline-block;width:8px;height:16px;background:var(--ink);vertical-align:-2px;animation:brass-blink 1s steps(2) infinite}
@keyframes brass-blink{50%{opacity:0}}
@media (prefers-reduced-motion:reduce){.app .typing{animation:none;opacity:.6}}
.app .empty-chat{max-width:760px;margin:6vh auto 0;text-align:left}
.app .empty-chat h1,.app .empty-chat .ask{margin-top:0;margin-bottom:10px}
.app .empty-chat .starter{grid-template-columns:repeat(2,minmax(0,1fr))}
.app .empty-chat .ramp-note{margin:16px 0}
.app .empty-chat .starter button{width:100%;justify-content:flex-start;text-align:left;min-height:64px;padding:14px 16px;border:1px solid var(--rule);background:var(--paper-light);color:var(--ink);font:400 var(--small)/1.45 var(--text)}
.app .empty-chat .starter button:hover{border-color:var(--ink);background:var(--paper-light)}
.app .composer{border-top:1px solid var(--rule);padding:16px 28px 20px;background:var(--paper)}
.app .composer form{max-width:760px;margin:0 auto}
.app .composer .box{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:end;border:1px solid var(--field-border,#857D6E);border-radius:2px;background:var(--paper-light);padding:8px 8px 8px 14px}
.app .composer .box:focus-within{border-color:var(--ink);outline:2px solid var(--brass-ink);outline-offset:1px}.app .composer .box{scroll-margin-bottom:12px}
.app .composer textarea{border:0!important;outline:0!important;background:transparent!important;padding:8px 0!important;min-height:28px!important;max-height:40vh;resize:none;font-size:var(--body)}
.app .composer button[type=submit]{min-height:44px;padding:0 20px}
.app .composer .under{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin-top:8px;font:400 12px/1.5 var(--mono);color:var(--soft)}
.app #chat-status{min-height:18px;margin:0;color:var(--error);font:400 12px/1.5 var(--mono);max-width:none}
/* A limit is set in the reading face, ink on paper with the brass rule; red stays for failures (visual, round 13). */
.app #chat-status.limit{font:400 var(--small)/1.5 var(--text);color:var(--ink);border-left:2px solid var(--brass-ink);padding:2px 0 2px 12px;margin-bottom:8px}
/* A link in a refusal stays whole ("Buy a pack" never splits; visual, round 14). */
.app #chat-status a{white-space:nowrap}
/* The empty-stash note is marked when a refusal repeats it, so Send visibly lands on it. */
.app .stash-empty.pressed{border-left:2px solid var(--brass-ink)}
/* A composer taller than 60% of the window is not pinned: the page scrolls, so the conversation stays in reach
   (accessibility, round 13). */
body.app.chat-page.composer-tall{height:auto;overflow:auto}
.app.chat-page.composer-tall .chat-shell{height:auto}
.app.chat-page.composer-tall #conversation{max-height:none}
.app.chat-page.composer-tall .chat-main{overflow:visible}
.app.chat-page.composer-tall .composer{position:static}
/* An empty status keeps its live region but gives back the gap, so the note under the composer starts flush. */
.app #chat-status:empty{margin-right:-12px}
/* The crisis line in the conversation, above any answer. */
.app .chat-message .crisis{margin:0 0 12px;padding:10px 14px;border-left:3px solid var(--brass-ink);background:var(--paper-light);font-size:var(--body);line-height:1.5;max-width:60ch}
/* The same line on the buy, send and gift pages, for a gift note the safety check read as about self-harm. */
.app .crisis{margin:0 0 16px;padding:10px 14px;border-left:3px solid var(--brass-ink);background:var(--paper-light);line-height:1.5;max-width:60ch;text-wrap:pretty}
.app #chat-status.info{color:var(--soft)}
.app .stash-empty{border:1px solid var(--rule);background:var(--paper-light);padding:14px 16px;margin:0 0 20px;font-size:var(--small);max-width:none}

/* Legal and admin */
.app .legal{max-width:720px}
.app .legal h2{font-size:26px;margin-top:40px}
.app .legal ul,.app .legal ol{padding-left:24px;margin:0 0 16px}
.app .legal li{font-size:var(--body);line-height:1.6;margin-bottom:6px}
.app table.admin{width:100%;border-collapse:collapse;font:400 12px/1.6 var(--mono)}
.app table.admin th,.app table.admin td{padding:10px 14px 10px 0;border-bottom:1px solid var(--rule);text-align:left;vertical-align:top}
.app table.admin th{font-weight:400;color:var(--soft)}
.app dl.totals{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:0 24px;max-width:560px;margin:16px 0 0;font:400 14px/1.6 var(--mono)}
.app dl.totals dt,.app dl.totals dd{margin:0;padding:10px 0;border-bottom:1px solid var(--rule)}
.app dl.totals dd{text-align:right}
.app pre.event{font:400 12px/1.5 var(--mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:6px 0 0;color:var(--soft)}
.app .plain-list{list-style:none;padding:0;margin:0}
.app .plain-list>li{padding:16px 0;border-top:1px solid var(--rule)}
.app .foot nav{flex-wrap:wrap}

@media (max-width:899px){
  .app .stage-split{grid-template-columns:1fr;gap:40px}
  .app .stage-split .solo{order:-1;width:min(360px,100%)}
  /* A page with an error keeps its form first, so the error and any 988 line open on the first screen (accessibility,
     round 13). */
  .app .stage-split:has(#form-error) .solo{order:0;margin-top:0}
  /* The envelope's canvas keeps empty room above it for the opening flap; on phones that room starts under the page's top
     padding, and on short phones the envelope is smaller, so the claim button sits higher (product, round 9). */
  .app .stage-split:not(.actions-first){gap:28px}
  .app .stage-split:not(.actions-first) .solo{margin-top:-28px}
  /* On the gift-ready page the link and its actions come first; the envelope follows, smaller. */
  .app .stage-split.actions-first .solo{order:0;width:min(280px,100%)}
  .app .chat-shell{grid-template-columns:1fr}
}
@media (max-width:640px) and (max-height:740px){.app .stage-split:not(.actions-first) .solo{width:min(300px,100%)}}
@media (max-width:640px){
  .app-main{padding-block:36px 80px}
  .app .packs{grid-template-columns:repeat(2,minmax(0,1fr))}
  .app .packs .pack-coin:last-child{grid-column:1/-1}
  .app .two{grid-template-columns:1fr}
  .app .check-address .three{grid-template-columns:1fr}
  .app .segmented{display:grid;grid-template-columns:1fr 1fr;width:100%}
  .app .segmented span{min-width:0}
  .app .banner{grid-template-columns:72px minmax(0,1fr);gap:14px}
  .app .banner img{width:72px}
  .app #conversation{padding:20px 16px 8px}
  .app .composer{padding:12px 16px 16px}
  .app .chat-bar{padding:10px 16px}
  .app .chat-message.user .message-body{max-width:92%}
  .app .empty-chat .starter{grid-template-columns:1fr}
  .app .top nav{gap:6px 18px;font-size:15px}
  .app button,.app .button{width:auto}
  .app .actions>.button,.app .actions>button,.app .actions>form,.app .actions>form>button{width:100%}
}
.app .pack-coin.c1 img{width:44px;height:44px}.app .pack-coin.c2 img{width:53px;height:53px}.app .pack-coin.c3 img{width:62px;height:62px}.app .pack-coin.c4 img{width:71px;height:71px}.app .pack-coin.c5 img{width:80px;height:80px}
.app .send-env{width:min(420px,100%);height:auto;display:block;margin:0 auto}
.app .claimed{border-top:1px solid var(--rule);margin-top:28px;padding-top:4px}
.app .claimed h2{margin-top:20px}
.app h1.lot{font:400 12px/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--brass-ink);margin:0 0 14px;max-width:none}
.app .coin-note{font-size:14px;color:var(--soft);margin:12px 0 0;max-width:60ch}
.app .buy-grid{display:grid;grid-template-columns:minmax(0,640px) minmax(240px,320px);gap:72px;align-items:start;justify-content:space-between}
.app .buy-aside{position:sticky;top:32px;border-top:1px solid var(--rule);padding-top:20px;margin-top:8px}
.app .buy-aside p{font-size:var(--small);color:var(--soft)}
.app .receipt-card{display:grid;grid-template-columns:auto 1fr;gap:0;margin:0 0 20px;padding:14px 16px;background:var(--paper-light);border:1px solid var(--rule);font:400 13px/1.9 var(--mono)}
.app .receipt-card dt{color:var(--soft)}
.app .receipt-card dd{margin:0;padding-left:16px;text-align:right}
.app .receipt-card dt:last-of-type,.app .receipt-card dd:last-of-type{border-top:1px solid var(--rule);padding-top:6px;margin-top:6px;color:var(--ink)}
@media (max-width:1000px){.app .buy-grid{grid-template-columns:1fr;gap:40px}.app .buy-aside{position:static}}
.app .chat-bar p{margin:0}
.app .composer .under{align-items:center}
.app .composer .under span{flex:1 1 auto}
.app .composer #delete-chat{font-size:13px;min-height:28px;color:var(--soft)}
.app .composer #delete-chat[hidden]{display:none}
/* The homepage hides secondary nav links on phones; app pages need them. */
@media (max-width:899px){.app .top nav a{display:inline}.app .top nav{gap:6px 16px;font-size:15px}.app .top .wrap{padding-block:12px}}
/* The chat is a full-height app view: header on top, the conversation scrolls, the composer stays put. */
body.app.chat-page{height:100dvh;display:flex;flex-direction:column;overflow:hidden}
.app.chat-page .top{flex:none}
.app.chat-page #main{flex:1 1 auto;min-height:0;padding:0}
.app .chat-shell{height:100%;min-height:0}
.app .chat-main{min-height:0;overflow:hidden}
.app .chat-side{overflow:hidden}
.app .convos{max-height:none;flex:1 1 auto;min-height:0}
.app .convos li{min-width:0}
.app .convos button{max-width:100%}
@media (max-width:899px){.app .chat-shell{grid-template-rows:minmax(0,1fr)}}
/* Wide screens: the conversation list sits at the side, the balance in the model bar, the rate in full. */
.app .chat-bar .convo-select,.app .phone-balance,.app .model-rate .short{display:none}
/* Phone chat (product, round 9): one bar above the conversation. Its first row holds the conversation menu, which starts
   with "New conversation", and the model menu; the rate keeps one line under them; the balance, with a gift week's room
   today, sits under the composer, where the week-one note's limit is felt. */
@media (max-width:899px){
  .app .chat-side{display:none}
  .app .chat-bar{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.9fr);column-gap:8px;row-gap:4px;align-items:center;padding:8px 16px}
  .app .chat-bar .convo-select{display:block;grid-column:1;grid-row:1;margin:0;min-width:0}
  .app .chat-bar .convo-select select{width:100%;min-height:40px;padding:6px 10px;font-size:16px}
  .app .model-pick{display:contents}
  .app .model-pick label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .app .model-pick select{grid-column:2;grid-row:1;max-width:none;width:100%;min-height:40px;font-size:16px;padding:6px 10px}
  .app .model-pick .model-rate{grid-column:1 / -1;grid-row:2}
  .app .chat-bar select{text-overflow:ellipsis}
  .app .model-rate .long{display:none}
  .app .model-rate .short{display:inline}
  .app .chat-bar #chat-balance{display:none}
  .app .composer .under{flex-wrap:wrap;row-gap:4px}
  .app .phone-balance{display:block;order:0;margin:0;font:400 13px/1.5 var(--mono);color:var(--ink)}
  .app .composer .under #chat-status{order:1}
  .app .composer #delete-chat{order:2;margin-left:auto}
  .app .composer .under span{order:3;flex-basis:100%;font-size:12px;line-height:1.4}
  /* Short phones: the empty state starts at the top and keeps its notes tight, so a starter shows whole above the composer. */
  .app #conversation{padding-top:16px}
  .app .empty-chat{margin-top:0}
  .app .empty-chat .ramp-note{margin:12px 0}
  .app .empty-chat .soft{font-size:var(--small);line-height:1.5;margin:0}
  .app .empty-chat .starter{margin-top:16px}
  .app .composer{padding:10px 16px 12px}
  .app .composer .box{padding:6px 6px 6px 12px}
  .app .composer .under{margin-top:6px}
}
/* App headers keep one row on phones from 360 px, as the homepage's does. */
@media (max-width:599px){.app .top{min-height:56px}.app .top .wrap{padding-block:8px;column-gap:12px}}
@media (max-width:420px){.app .top nav{gap:6px 12px;font-size:14px}.app .top nav .link-button{font-size:inherit}}
/* The rate keeps one line down to 350 px and wraps below it, so no figure is ever cut. */
@media (max-width:399px){.app .model-pick .model-rate{font-size:11px}.app .chat-bar .convo-select select,.app .chat-bar .model-pick select{padding-inline:6px}}
/* iPhones are 375 px and wider, where 16 px menus keep Safari from zooming on a tap; Android's Chrome never zooms, so
   narrower screens take 15 px and keep "New chat" and the longest model name whole. */
@media (max-width:374px){.app .chat-bar .convo-select select,.app .chat-bar .model-pick select{font-size:15px}}
@media (max-width:349px){.app .model-pick .model-rate{white-space:normal;grid-row:3}.app .chat-bar{grid-template-columns:minmax(0,1fr)}.app .model-pick select{grid-column:1;grid-row:2}}
.app .message-body table{display:block;max-width:100%;overflow-x:auto}
.app .message-body th,.app .message-body td{white-space:nowrap}
/* The first week's sentence on /buy after a checkout was refused once for it (visual, round 14). */
.app .hint.marked{border-left:2px solid var(--brass-ink);padding-left:12px;color:var(--ink)}
/* The first week's lines sit between the total and the Terms box, before the button that pays (loyalty, round 14). */
.app #own-week,.app #new-card-week,.app #week-gift{margin:0 0 14px}
.app .ramp-note{font-size:var(--small);line-height:1.5;color:var(--soft);border-left:2px solid var(--strong);padding:2px 0 2px 12px;margin:18px 0 0;max-width:70ch}
.app .composer .ramp-note{margin:0 0 10px}
.app .small-copy{display:flex;gap:8px;margin:10px 0 0}
.app .small-copy input{min-height:38px!important;font-size:12px!important;padding:6px 8px!important}
.app .small-copy button{min-height:38px;padding:0 10px;font-size:12px}
/* Round-1 panel fixes */
.app{--field-border:#857D6E}
.app input:not([type=radio]):not([type=checkbox]):not([type=hidden]),.app textarea,.app select{border-color:var(--field-border)}
.app .choice input{accent-color:var(--ink)}
.app .field>label{display:block;font-size:var(--small);margin-bottom:8px}
.app .hint{font-size:var(--small);color:var(--soft);margin:8px 0 0;max-width:62ch}
.app .hint.claim-rule{margin-bottom:18px}.app .hint .ink{color:var(--ink)}
/* Long email addresses and links wrap instead of pushing the page sideways at 320 px. */
.app p,.app .solo-caption{overflow-wrap:anywhere}
.app .feedback.error p{margin:0}
.app input[aria-invalid=true],.app select[aria-invalid=true],.app textarea[aria-invalid=true]{border-color:var(--error);outline:1px solid var(--error)}
.app .entity{font-size:var(--small)}
/* Keyboard-reachable scroll areas */
.app .table-scroll:focus-visible{outline:2px solid var(--brass-ink);outline-offset:2px}
.app .message-body table{display:table;max-width:none}
.app .message-body .table-scroll{margin:0 0 18px;overflow-x:auto}
.app .message-body .table-scroll table{margin:0}
.app .message-body .code-wrap{position:relative;margin:0 0 18px}
.app .message-body .code-wrap pre{margin:0}
.app .message-body th,.app .message-body td{white-space:normal;min-width:6ch}
/* Admin */
.app .gate-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:12px;margin:12px 0 24px}
.app .gate-grid div{border:1px solid var(--rule);background:var(--paper-light);padding:14px 16px}
.app .gate-grid dt{font-size:13px;color:var(--soft);line-height:1.35}
.app .gate-grid dd{margin:6px 0 0;font:400 28px/1.1 var(--display)}
.app .inline-form{display:flex;flex-wrap:wrap;gap:8px;align-items:center;max-width:none}
.app .inline-form input,.app .inline-form select{width:auto!important;min-height:40px!important;padding:6px 10px!important;font-size:15px!important}
/* The check field matches its button's height (visual, round 21). */
.app .inline-form input,.app .inline-form button{min-height:44px!important}
.app .settings fieldset{margin:8px 0 16px}
.app .cashout{margin-top:8px}
.app .cashout textarea{min-height:84px}
/* When no size fits, one line in ink says when more does (visual, round 21). */
.app .no-size{margin:24px 0 8px}.app .no-size .ink-line{color:var(--ink);margin:0 0 12px;max-width:62ch}
/* A hint above the address field keeps the gift fields' 8 px from the label under it (accessibility, round 20). */
.app .cashout>.hint{margin-bottom:8px}
/* Short or zoomed viewports: the chat scrolls as a page instead of trapping the composer */
@media (max-height:600px){body.app.chat-page{height:auto;overflow:auto}.app .chat-shell{height:auto}.app #conversation{max-height:none}}
/* From 480 to 600 px tall (an iPhone SE in Safari with its toolbar showing) the page scrolls, and the composer stays pinned
   to the bottom, so the message box is always in reach and a focused link never sits under it (product, round 10). Under
   480 px, which only zoom reaches, the page simply scrolls. */
@media (min-height:480px) and (max-height:600px){.app .chat-main{overflow:visible}.app .composer{position:sticky;bottom:0;z-index:2}:root:has(body.chat-page){scroll-padding-bottom:var(--composer-height,180px)}}
/* An unpinned composer leaves no scroll padding behind it (accessibility, round 14). */
:root:has(body.composer-tall){scroll-padding-bottom:0}
/* Round 2: the eyebrow and the footer keep their sizes on app pages (.app p would otherwise win). */
.app p.lot,.app h1.lot{font:400 12px/1.4 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--brass-ink);margin:0 0 14px}
.app .foot p{font-size:var(--small);line-height:1.5}
.app .composer textarea::placeholder{color:var(--soft);opacity:1}
.app #conversation:focus-visible{outline:2px solid var(--brass-ink);outline-offset:-2px}
.app .message-body pre .copy:focus-visible{outline:2px solid #EDE8DC;outline-offset:2px}
.app .pack-coin.over{opacity:.4;cursor:not-allowed}
.app .pack-coin.over:hover{border-color:var(--rule);background:transparent}
.app .notice-line{border-left:2px solid var(--brass-ink);padding:6px 0 6px 12px;margin:0 0 18px}
.app .admin-search{margin:0 0 24px}
.app .admin-search label{font:400 12px var(--mono);text-transform:uppercase;letter-spacing:.08em;color:var(--brass-ink)}
@media (max-width:599px){.app .copy-link{display:grid;grid-template-columns:1fr}.app .copy-link button{width:100%}}
/* Phones and 200% zoom: stash tables become labelled rows, so the cost and the date stay in view. */
@media (max-width:639px){
  .app table.receipts thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .app table.receipts,.app table.receipts tbody,.app table.receipts tr,.app table.receipts td{display:block;width:100%}
  .app table.receipts tr{padding:8px 0;border-bottom:1px solid var(--rule)}
  .app table.receipts td{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:2px 0;border:0;white-space:normal;text-align:right}
  .app table.receipts td.num{padding-right:0}
  .app table.receipts td::before{content:attr(data-label);flex:none;font:400 11px/1.6 var(--mono);letter-spacing:.06em;text-transform:uppercase;color:var(--soft);text-align:left}
}
.app .gift-fields>.hint{margin:0 0 8px;max-width:34em}
.app .gift-fields>.hint:last-of-type{margin-bottom:18px}
/* Focus rings inside scrolling tables sit inside the region, so its edge does not clip them. */
.app .table-scroll a:focus-visible{outline-offset:-2px}
/* Windows contrast themes drop backgrounds and shadows: the chosen pack, the chosen recipient and the open conversation
   need marks that survive forced colors. */
@media (forced-colors:active){
  .app .pack-coin:has(input:checked){border-width:3px;padding:16px 6px 14px}
  .app .segmented input:checked+span{forced-color-adjust:none;background:Highlight;color:HighlightText}
  .app .segmented input:checked:focus-visible+span{outline-color:HighlightText}
  .app .convos button[aria-current=true]{border-left:3px solid CanvasText}
}
/* The account a claim goes to, with a way to switch before claiming. */
.app .claim-as{display:flex;flex-wrap:wrap;align-items:center;gap:0 12px;margin:0 0 12px}
.app .claim-as p{margin:0}
.app .claim-as .link-button{font-size:var(--small);min-height:44px;text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--strong)}
/* Visual, round 10: Sign out matches the links beside it at every width; hints, starters and the composer's note never end on
   one word alone. */
.app .top nav .link-button{font-size:inherit}
.app .hint,.app .starter button,.app .composer .under span{text-wrap:pretty}
