/*
 * The app's styles. Shares its tokens with the landing page deliberately —
 * a person arriving from medbillanalyzer.com should not feel handed off to a
 * different product at the moment they upload a medical bill.
 *
 * The contrast values here were solved against the backgrounds each token
 * actually sits on, not eyeballed. --faint clears 4.6:1 in both palettes; see
 * the accessibility pass on the landing page for the arithmetic.
 */

:root{
  --ground:#F4F7F6; --surface:#FFFFFF; --sunk:#EAEFED;
  --ink:#15211F; --muted:#5C6B67; --faint:#606D69;
  --line:#DCE4E1; --line-strong:#C3CFCB;
  --accent:#0E6F63; --accent-soft:#D6EAE6;
  --attention:#B0532A; --attention-soft:#F6E2D6;
  --positive:#2E7D5B; --positive-soft:#DCEFE5;
  --radius:12px;
  --measure:64ch;
}
@media (prefers-color-scheme: dark){ :root:not([data-theme="light"]){
  --ground:#0F1514; --surface:#18221F; --sunk:#131C1A;
  --ink:#E7EEEB; --muted:#94A29E; --faint:#7C8C87;
  --line:#26332F; --line-strong:#33433E;
  --accent:#4FC0AE; --accent-soft:#12312D;
  --attention:#DB8A55; --attention-soft:#332014;
  --positive:#57AE82; --positive-soft:#14291F;
}}
:root[data-theme="dark"]{
  --ground:#0F1514; --surface:#18221F; --sunk:#131C1A;
  --ink:#E7EEEB; --muted:#94A29E; --faint:#7C8C87;
  --line:#26332F; --line-strong:#33433E;
  --accent:#4FC0AE; --accent-soft:#12312D;
  --attention:#DB8A55; --attention-soft:#332014;
  --positive:#57AE82; --positive-soft:#14291F;
}

*{box-sizing:border-box}
body{margin:0;background:var(--ground);color:var(--ink);
  font-family:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:16px;line-height:1.6;-webkit-font-smoothing:antialiased}
.wrap{max-width:900px;margin:0 auto;padding-inline:20px}
.mono{font-family:"IBM Plex Mono",ui-monospace,SFMono-Regular,monospace;font-variant-numeric:tabular-nums}
a{color:var(--accent)}
a:focus-visible,button:focus-visible,input:focus-visible,pre:focus-visible,label:focus-visible{
  outline:2px solid var(--accent);outline-offset:3px}

/* Off-screen until focused, not display:none — hiding it would take it out of
   the tab order and defeat the point of having it. */
.skip{position:absolute;left:-9999px;top:0;z-index:10;
  background:var(--surface);color:var(--ink);border:1px solid var(--line-strong);
  border-radius:0 0 var(--radius) 0;padding:12px 18px;text-decoration:none;font-weight:600}
.skip:focus{left:0}

header{padding-block:22px;border-bottom:1px solid var(--line)}
header .wrap{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:11px;font-weight:600;font-size:17px;
  text-decoration:none;color:var(--ink);min-height:24px}
.brand .a{color:var(--accent)}
.local{margin-left:auto;font-size:12.5px;color:var(--faint);
  border:1px solid var(--line);border-radius:999px;padding:4px 11px}

main{padding-block:38px 56px;min-height:60vh}
h1{font-family:"IBM Plex Serif",Georgia,serif;font-weight:700;
  font-size:clamp(26px,4.4vw,40px);line-height:1.12;letter-spacing:-.02em;
  margin:0 0 14px;text-wrap:balance;max-width:22ch}
h2{font-size:17px;margin:0 0 6px}
.lede{font-size:clamp(16px,2vw,18px);color:var(--muted);max-width:58ch;margin:0 0 26px}
.eyebrow{font-size:11.5px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--accent);margin:0 0 10px}
.eyebrow.bad{color:var(--attention)}
.hint{font-size:14px;color:var(--muted);margin:0 0 14px;max-width:52ch}
.caption{font-size:13px;color:var(--faint);margin-top:18px;max-width:var(--measure)}
.disclaimer{margin-top:44px;padding-top:18px;border-top:1px solid var(--line);
  font-size:12.5px;color:var(--faint);max-width:var(--measure)}

/* ---------- upload ---------- */
/* `hidden` must win over any display rule below. Without this the
   upload cards stayed on screen with hidden=true set — the attribute's
   UA `display:none` loses to an explicit one, and the DOM read looked
   correct while the page showed a file picker we had just disabled. */
[hidden]{display:none !important}
.drops{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));margin-bottom:24px}
.drop{background:var(--surface);border:1px dashed var(--line-strong);border-radius:var(--radius);padding:20px}
.drop.over{border-color:var(--accent);background:var(--accent-soft)}
.drop.filled{border-style:solid;border-color:var(--accent)}
.opt{font-weight:400;font-size:12.5px;color:var(--faint)}
.pick{display:inline-flex;align-items:center;min-height:38px;padding:0 16px;cursor:pointer;
  background:var(--sunk);border:1px solid var(--line-strong);border-radius:8px;
  font-size:14.5px;font-weight:600;color:var(--ink)}
.pick:hover{border-color:var(--accent)}
/* The input is driven by its label; hiding it with display:none would remove
   it from the accessibility tree along with the keyboard path to it. */
.drop input[type=file]{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
/* The input is 1px and opacity:0, so its own focus ring is invisible. This
   rule used to remove an outline from .chosen — the status paragraph, which
   is not what a keyboard user is looking at — leaving the one control they
   can see with no focus indication at all. Put the ring on the label, which
   IS the visible control. */
.drop:has(input[type=file]:focus-visible) .pick{
  outline:2px solid var(--accent);outline-offset:3px}
.chosen{font-size:13.5px;color:var(--accent);margin:12px 0 0;min-height:20px;word-break:break-word}

.actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:8px}
button{font:inherit;cursor:pointer;border-radius:8px;min-height:44px;padding:0 20px;
  font-size:15px;font-weight:600;border:1px solid transparent}
button:disabled{opacity:.5;cursor:not-allowed}
.primary{background:var(--accent);color:#fff}
.primary:hover:not(:disabled){filter:brightness(1.08)}
.secondary{background:var(--surface);color:var(--ink);border-color:var(--line-strong)}
.secondary:hover{border-color:var(--accent)}
.ghost{background:transparent;color:var(--accent);border-color:var(--line);min-height:32px;
  padding:0 12px;font-size:13.5px}
.ghost:hover{border-color:var(--accent)}

/* ---------- working ---------- */
.working{padding-block:40px;text-align:center}
.bars{display:flex;gap:6px;justify-content:center;margin-bottom:22px}
.bars i{display:block;width:34px;height:9px;border-radius:5px;background:var(--line-strong)}
.bars i:nth-child(2){width:22px}
.bars i:nth-child(3){width:14px;background:var(--accent)}
@media (prefers-reduced-motion: no-preference){
  .bars i{animation:pulse 1.4s ease-in-out infinite}
  .bars i:nth-child(2){animation-delay:.18s}
  .bars i:nth-child(3){animation-delay:.36s}
  @keyframes pulse{0%,100%{opacity:.35}50%{opacity:1}}
}

/* ---------- summary ---------- */
.tally{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin-bottom:26px}
.stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:16px 18px}
.stat .v{font-family:"IBM Plex Mono",monospace;font-variant-numeric:tabular-nums;
  font-size:26px;font-weight:600;line-height:1.1;display:block}
.stat .k{font-size:12.5px;color:var(--muted);margin-top:6px;display:block}
.stat.money .v{color:var(--attention)}
.stat.good .v{color:var(--positive)}
/* A clean-but-incomplete check. Deliberately not the positive colour. */
.stat.partial .v{color:var(--muted)}

/* ---------- findings ---------- */
.findings{list-style:none;margin:0 0 34px;padding:0;display:grid;gap:14px}
.finding{background:var(--surface);border:1px solid var(--line);border-left:4px solid var(--line-strong);
  border-radius:var(--radius);padding:18px 20px}
.finding.discrepancy{border-left-color:var(--attention)}
.finding.worth_asking{border-left-color:var(--accent)}
.finding h3{margin:0 0 4px;font-size:16.5px;line-height:1.35}
.fmeta{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px;
  font-size:12px;letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}
.tag{border-radius:999px;padding:2px 9px;font-weight:600;letter-spacing:.06em}
.tag.discrepancy{background:var(--attention-soft);color:var(--attention)}
.tag.worth_asking{background:var(--accent-soft);color:var(--accent)}
.famount{font-family:"IBM Plex Mono",monospace;font-size:15px;font-weight:600;
  color:var(--attention);text-transform:none;letter-spacing:0}
.finding p{margin:0 0 10px;font-size:15px;color:var(--muted);max-width:var(--measure)}
.ask{background:var(--sunk);border-radius:8px;padding:12px 14px;font-size:14.5px;color:var(--ink)}
.ask b{display:block;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--faint);margin-bottom:4px}
.cites{font-size:12.5px;color:var(--faint);margin-top:10px}

/* ---------- the free peek at what was found ----------
   The summary screen showed three numbers and a button, which is asking
   someone to buy a list they have not seen. These are the finding titles,
   free. Visually quieter than .finding on purpose: same left-edge tier
   colour so the two screens read as the same list, but flat rather than
   carded, because this is the index and the cards are the thing bought.
   The row is NOT a link or a button — nothing here is clickable, and
   making it look tappable would promise detail that is not behind it. */
.peek{list-style:none;margin:0 0 26px;padding:0;display:grid;gap:1px;
  background:var(--line);border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden}
.peek li{background:var(--surface);padding:13px 16px;display:flex;gap:14px;
  align-items:baseline;border-left:3px solid var(--line-strong)}
.peek li.discrepancy{border-left-color:var(--attention)}
.peek li.worth_asking{border-left-color:var(--accent)}
.peek .ptitle{flex:1 1 auto;font-size:15px;line-height:1.4;min-width:0}
.peek .pamount{font-family:"IBM Plex Mono",monospace;font-size:14.5px;font-weight:600;
  color:var(--attention);white-space:nowrap}
.peek .ptier{font-size:11px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--faint);white-space:nowrap}
/* Under ~480px the three columns crush the title to two words a line. */
@media (max-width:480px){
  .peek li{flex-wrap:wrap;gap:6px 12px}
  .peek .ptitle{flex:1 1 100%}
}
/* Sits between the list and the buy button, so it reads as a caption on the
   list rather than as small print under the button. */
.peeknote{margin:-16px 0 24px;max-width:var(--measure)}

/* ---------- outputs ---------- */
.outputs{border-top:1px solid var(--line);padding-top:28px}
.nameform label{display:block;font-size:14px;color:var(--muted);margin-bottom:8px}
.nameform .row{display:flex;gap:10px;flex-wrap:wrap}
.nameform input,.nameform textarea{flex:1 1 220px;min-height:44px;padding:0 14px;font:inherit;
  background:var(--surface);color:var(--ink);border:1px solid var(--line-strong);border-radius:8px}
/* Both fields sit directly in the form now rather than inside .row, so they
   need their own full width; the flex-basis above only applies in a row. */
.nameform input,.nameform textarea{display:block;width:100%;box-sizing:border-box}
.nameform textarea{padding:12px 14px;line-height:1.5;resize:vertical;
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:14px}
.nameform>label:not(:first-child){margin-top:18px}
.nameform .hint{margin:0 0 8px}
.nameform .row{margin-top:16px}
.doc{margin-top:24px}
.dochead{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:10px}
.dochead h3{margin:0;font-size:16px;flex:1 1 auto}
pre{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:18px;overflow-x:auto;font-family:"IBM Plex Mono",ui-monospace,monospace;
  font-size:13.5px;line-height:1.65;white-space:pre-wrap;margin:0;max-height:60vh;overflow-y:auto}

footer{padding-block:28px 48px;font-size:13.5px;color:var(--faint);border-top:1px solid var(--line)}
footer .wrap{display:flex;gap:22px;flex-wrap:wrap;align-items:baseline}
footer a{color:var(--muted);text-decoration:none;display:inline-flex;align-items:center;min-height:24px}
footer a:hover{color:var(--ink)}
footer .right{margin-left:auto}

@media (max-width:520px){
  .actions button{flex:1 1 100%}
}

/* The delete code and its control. Deliberately quiet: it is a right, not
   a call to action, and it sits below the result it belongs to. */
.delete-box{margin-top:28px;padding-top:18px;border-top:1px solid var(--line)}
.delete-box .mono{font-family:"IBM Plex Mono",monospace;font-size:14px;
  word-break:break-all;margin:6px 0 12px}
button.quiet{background:none;border:1px solid var(--line);color:var(--muted);
  border-radius:var(--radius);padding:9px 14px;font:inherit;font-size:14px;cursor:pointer}
button.quiet:hover{color:var(--ink);border-color:var(--ink)}

/* ---------- the membership code ----------
   A credential the server cannot reissue, so it is displayed like one:
   boxed, monospace, selectable, with its own copy control. Not quiet like
   the delete code — losing this one means having paid for nothing. */
.codebox{background:var(--sunk);border:1px solid var(--line-strong);
  border-radius:var(--radius);padding:16px 18px;margin:0 0 22px}
.codebox .mono{font-size:15px;font-weight:600;word-break:break-all;
  margin:0 0 12px;user-select:all}
.codebox .actions{margin-top:0}
.codebox.recall{background:var(--accent-soft);border-color:var(--accent);margin-bottom:28px}
.codebox.recall .caption{margin-top:0;margin-bottom:8px;color:var(--ink)}

/* The two ways to pay, side by side. The monthly one is the primary
   button because it is the same price and covers 25 bills instead of
   one — see the note this row carries. */
.plans{display:grid;gap:10px;margin-top:8px}
.plans .row{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.plans .why{font-size:13px;color:var(--faint);max-width:var(--measure);margin:2px 0 0}
