/* winsorlabs shared components — the one copy every app ships, after tokens.css.
   Canonical source: build-an-app/assets/components.css in catjwin/claude-skills.
   Copy it unchanged next to tokens.css; never edit an app's copy.
   Picked 15 Oct 2026 (component picks 1D 2A+2B 3A 4C 5B 6A 7B 8B 9D 10D 11B 12B).
   Every rule is one class deep (inputs use :where(), so no specificity at all),
   so an app's own rule, written after this file, always wins. */

/* The page itself never bounces past its ends: on iOS the overshoot dragged the
   sticky header up under the status bar, where the system blurs it. Scrolling
   is unchanged; panels and lists keep their own overscroll. */
html, body { overscroll-behavior-y:none; }

/* 0 · App frame — opt in with <html class="frame"> and wl.frame(). The page itself
   never scrolls: the header stays put and only <main> scrolls, so nothing slides up
   under the iOS status bar, which blurs whatever sits under it (header included).
   The body and full-screen panels are pinned to the visible area (--vvh/--vvtop,
   kept current by wl.frame()), so the keyboard can't push the header off the top
   either. Fixed bars (tabs, toasts, sheets) sit over it as before. The one place
   this file reaches past one class: it has to beat each app's sticky header. */
html.frame { height:100%; overflow:hidden; overscroll-behavior:none; }
html.frame body { position:fixed; left:0; right:0; top:var(--vvtop,0px); height:var(--vvh,100%); min-height:0;
  overflow:hidden; display:flex; flex-direction:column; }
html.frame body > header { flex:none; position:relative; top:auto; backdrop-filter:none; -webkit-backdrop-filter:none; }
html.frame body > main { flex:1; min-height:0; width:100%; overflow-y:auto; overscroll-behavior-y:contain; }
html.frame .panel { top:var(--vvtop,0px); bottom:auto; height:var(--vvh,100%); }

/* Icons from icons.js: text-sized and inline unless a component sizes them. */
svg.ph { width:1em; height:1em; display:inline-block; vertical-align:-.125em; flex:none; }

/* 1 · Primary button — the one commit action. 46px, pill. */
.btn { display:inline-flex; align-items:center; justify-content:center; gap:.4rem;
  min-height:var(--tap-min); padding:var(--pad-button) 1.4rem; border:0; border-radius:var(--radius-pill);
  background:var(--action-primary); color:var(--text-on-primary); box-shadow:var(--shadow-primary);
  font:var(--fw-bold) var(--text-body-size)/1.2 var(--font-body); text-decoration:none; text-align:center;
  cursor:pointer; transition:var(--transition-button); }
.btn:active { transform:var(--press-shift); background:var(--action-primary-press); }
.btn:disabled { opacity:.45; box-shadow:none; cursor:default; transform:none; }
.btn.full { width:100%; }
/* the one small size: panel headers and tight rows */
.btn.small { min-height:2.3rem; padding:.35rem .95rem; font-size:.9rem; box-shadow:none; }

/* 2 · Secondary buttons. Quiet is the standard; voice is the app offering something. */
.btn.quiet { background:var(--raised); color:var(--ink2); border:1px solid var(--line); box-shadow:none; }
.btn.quiet:active { background:var(--panel); }
.btn.voice { background:var(--accent-tint); color:var(--accent-light); border:1px solid var(--accent-line); box-shadow:none; }
.btn.voice:active { background:color-mix(in srgb, var(--accent) 20%, transparent); }
.btn.danger { color:var(--bad); border-color:var(--bad-line); }
/* two-tap confirm: the first tap arms (a toast says what the next does). Red when
   the second tap removes something; violet when it goes ahead with something. */
.btn.armed, .armed { color:var(--bad); background:var(--bad-tint); border-color:var(--bad-line); box-shadow:none; }
.btn.armed-go, .armed-go { color:var(--primary-ink); background:var(--primary-tint); border-color:var(--primary); box-shadow:none; }

/* 3 · Selectable chips — tap to choose. Dim until picked. */
.chips { display:flex; flex-wrap:wrap; gap:.35rem; }
.chip { display:inline-flex; align-items:center; justify-content:center; gap:.3rem;
  min-height:2.5rem; padding:.4rem .75rem; border-radius:var(--radius-pill);
  background:var(--raised); border:1px solid var(--line); color:var(--dim);
  font:600 .84rem/1.2 var(--font-body); cursor:pointer; }
.chip.on { background:var(--primary-tint); border-color:var(--primary); color:var(--primary-ink); }
.chip.small { min-height:2rem; padding:.2rem .65rem; font-size:.8rem; }

/* 4 · Label chips — not tappable: a status, a type, a count. Tones by job. */
.tag { display:inline-flex; align-items:center; gap:.3rem; padding:.2rem .6rem; border-radius:var(--radius-pill);
  font:600 var(--text-2xs)/1.3 var(--font-body); white-space:nowrap;
  background:var(--raised); color:var(--dim); border:1px solid var(--line); }
.tag.data { background:var(--neon-tint); color:var(--neon); border-color:color-mix(in srgb, var(--neon) 35%, transparent); }
.tag.warn { background:var(--warn-tint); color:var(--warn); border-color:transparent; }
.tag.good { background:var(--good-tint); color:var(--good); border-color:transparent; }
.tag.bad { background:var(--bad-tint); color:var(--bad-ink); border-color:transparent; }
.tag.type { background:var(--primary-tint); color:var(--primary-ink); border-color:transparent; }

/* 5 · Section label — the uppercase line that divides a screen. */
.eyebrow { display:flex; justify-content:space-between; align-items:baseline; gap:.6rem;
  margin:1.6rem 0 .6rem; color:var(--dim);
  font:var(--type-label); letter-spacing:var(--track-label); text-transform:uppercase; }
.eyebrow.warn { color:var(--warn); }   /* this section needs attention */
.eyebrow .hint { letter-spacing:0; text-transform:none; font-weight:var(--fw-medium); color:var(--dim2); }

/* 6 · Full-screen panel: fixed header (Back · title · action), only the body scrolls. */
.panel { position:fixed; inset:0; z-index:30; background:var(--bg); display:flex; flex-direction:column; }
.panel-head { flex:none; display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:.5rem;
  padding:calc(.55rem + env(safe-area-inset-top)) .8rem .55rem; background:var(--panel); border-bottom:1px solid var(--line); }
.panel-head h2 { margin:0; text-align:center; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  font:var(--fw-black) 1.05rem/1.2 var(--font-display); letter-spacing:-.01em; }
.panel-head h2.data { font:var(--fw-bold) .85rem/1.2 var(--font-body); letter-spacing:0; color:var(--neon); font-variant-numeric:tabular-nums; }
.panel-head .back { min-width:4.2rem; padding:.5rem .3rem; background:none; border:0; text-align:left; cursor:pointer;
  color:var(--accent); font:600 .95rem var(--font-body); }
.panel-head .act { min-width:4.2rem; display:flex; justify-content:flex-end; }
.panel-body { flex:1; overflow-y:auto; -webkit-overflow-scrolling:touch; overscroll-behavior:contain;
  width:100%; max-width:var(--measure); margin:0 auto; padding:.9rem 1rem calc(1.5rem + env(safe-area-inset-bottom)); }
body.panel-open { overflow:hidden; }

/* 7 · Empty state — two short lines: <div class="empty"><b>No trips yet</b>Scan your inbox…</div> */
.empty { text-align:center; color:var(--dim); padding:3rem 1rem; line-height:1.6; }
.empty b { display:block; margin-bottom:.4rem; color:var(--text); font:var(--type-title); }

/* 8 · Sign-in — the app's name, one line, the primary button. */
.signin { text-align:center; padding:4rem 1rem; }
.signin h1 { margin:0 0 .4rem; font:var(--fw-black) 1.7rem/1.1 var(--font-display); letter-spacing:var(--track-display); }
.signin p { margin:0 0 1.6rem; color:var(--dim); }

/* 9 · Text inputs — raised, a 1.5px border you can find on a dark screen, 44px tall. */
:where(input:not([type=checkbox], [type=radio], [type=range], [type=file], [type=color], [type=button], [type=submit]), select, textarea) {
  width:100%; min-height:var(--tap-min); padding:.8rem; border-radius:var(--radius-sm);
  background:var(--raised); color:var(--text); border:var(--border-input) solid var(--line-strong);
  font:var(--type-body); font-size:16px; outline:none; }
:where(input, select, textarea):focus { border-color:var(--primary); box-shadow:0 0 0 4px var(--primary-tint); }
:where(input, textarea)::placeholder { color:var(--dim2); }

/* 10 · Banner — one shape, four tones by job. Plain text (with <br> or <b>) flows as a
   block; with a .dismiss button at the end it becomes a row. */
.banner { display:block; margin:.8rem 0; padding:.7rem .9rem;
  border-radius:var(--radius); font-size:var(--text-xs); line-height:1.45; }
.banner:has(> .dismiss) { display:flex; align-items:flex-start; gap:.6rem; }
.banner.warn { background:var(--warn-tint); border:1px solid var(--warn-line); color:var(--warn); }
.banner.bad { background:var(--bad-tint); border:1px solid var(--bad-line); color:var(--bad-ink); }
.banner.voice { background:var(--accent-tint); border:1px solid var(--accent-line); color:var(--accent-light); }
.banner.data { background:var(--neon-tint); border:1px solid color-mix(in srgb, var(--neon) 35%, transparent); color:var(--neon); }
.banner .dismiss { margin-left:auto; flex:none; padding:0 .2rem; background:none; border:0; color:inherit; cursor:pointer; font:inherit; }

/* 11 · Toast — above everything; 2.6s for a message, 6s with Undo (set in the app's toast()). */
.toast { position:fixed; left:50%; transform:translateX(-50%); bottom:calc(5.4rem + env(safe-area-inset-bottom)); z-index:50;
  display:flex; gap:.8rem; align-items:center; max-width:calc(100% - 2rem);
  padding:.6rem .6rem .6rem 1.1rem; border-radius:var(--radius-pill);
  background:var(--raised); border:1px solid var(--line-strong); box-shadow:var(--shadow-float); font-size:.9rem; }
.toast:not(:has(button)) { padding-right:1.1rem; }
.toast button { padding:.4rem .8rem; border:0; border-radius:var(--radius-pill); cursor:pointer;
  background:var(--primary-tint); color:var(--primary-ink); font-weight:var(--fw-bold); }

/* 12 · Version footer — "health 261015-d · you@… · Sign out" */
.appver { margin:2rem 0 1rem; text-align:center; color:var(--dim2); font-size:.75rem; font-variant-numeric:tabular-nums; }
.appver a { color:var(--accent); font-weight:600; text-decoration:none; }
