/* ============================================================================
   Cotton Disease AI

   The content is photographs and numbers. The interface should recede: almost no
   borders, separation by space and a half-step of surface tone, one accent, and
   type doing the hierarchy rather than boxes and labels.

   No web fonts — the app is meant to run offline on field hardware, and a font
   that fails to load is worse than one never requested.
   ============================================================================ */

:root {
  color-scheme: light;

  --bg:        #f7f7f6;
  --surface:   #ffffff;
  --surface-2: #f2f2f1;
  --surface-3: #ebebe9;
  --line:      rgba(28, 25, 23, .08);
  --line-2:    rgba(28, 25, 23, .15);

  --ink:       #171615;
  --ink-2:     #46433f;
  --muted:     #8a847d;
  --faint:     #b5afa8;

  --accent:      #1a7f4b;
  --accent-2:    #14603a;
  --accent-ink:  #ffffff;
  --accent-soft: rgba(26, 127, 75, .10);

  /* A chip resting on a recessed track. --surface does this in light, but in dark it
     is DARKER than --surface-2, so selected chips read as pressed in rather than up. */
  --raised:    #ffffff;

  --ok:      #1a7f4b;  --ok-soft:     rgba(26,127,75,.10);
  --info:    #2563eb;  --info-soft:   rgba(37,99,235,.10);
  --warn:    #b45309;  --warn-soft:   rgba(180,83,9,.10);
  --danger:  #c0332b;  --danger-soft: rgba(192,51,43,.10);

  --r-xs: 8px; --r-sm: 10px; --r: 14px; --r-lg: 20px; --r-full: 999px;

  --sh-1: 0 1px 2px rgba(20,18,16,.04);
  --sh-2: 0 1px 1px rgba(20,18,16,.03), 0 6px 16px -6px rgba(20,18,16,.10);
  --sh-3: 0 12px 40px -12px rgba(20,18,16,.22);

  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --bar-h: 60px;
  --pad: 28px;
  --tabbar-h: 0px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --tap: 44px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg:        #0d0d0c;
    --surface:   #171716;
    --surface-2: #1e1e1c;
    --surface-3: #262624;
    --line:      rgba(255, 255, 255, .07);
    --line-2:    rgba(255, 255, 255, .14);

    --ink:       #f4f3f1;
    --ink-2:     #cfccc7;
    --muted:     #948f88;
    --faint:     #6a655f;

    --accent:      #35bd74;
    --accent-2:    #4dd68b;
    --accent-ink:  #041e10;
    --accent-soft: rgba(53, 189, 116, .15);
    --raised:    #302f2c;

    --ok:      #35bd74;  --ok-soft:     rgba(53,189,116,.15);
    --info:    #7aa7ff;  --info-soft:   rgba(122,167,255,.13);
    --warn:    #f0b429;  --warn-soft:   rgba(240,180,41,.13);
    --danger:  #ef7c74;  --danger-soft: rgba(239,124,116,.13);

    --sh-1: 0 1px 2px rgba(0,0,0,.5);
    --sh-2: 0 1px 1px rgba(0,0,0,.4), 0 6px 16px -6px rgba(0,0,0,.6);
    --sh-3: 0 12px 40px -12px rgba(0,0,0,.8);
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:        #0d0d0c;
  --surface:   #171716;
  --surface-2: #1e1e1c;
  --surface-3: #262624;
  --line:      rgba(255, 255, 255, .07);
  --line-2:    rgba(255, 255, 255, .14);
  --ink:       #f4f3f1;
  --ink-2:     #cfccc7;
  --muted:     #948f88;
  --faint:     #6a655f;
  --accent:      #35bd74;
  --accent-2:    #4dd68b;
  --accent-ink:  #041e10;
  --accent-soft: rgba(53, 189, 116, .15);
  --raised:    #302f2c;
  --ok:      #35bd74;  --ok-soft:     rgba(53,189,116,.15);
  --info:    #7aa7ff;  --info-soft:   rgba(122,167,255,.13);
  --warn:    #f0b429;  --warn-soft:   rgba(240,180,41,.13);
  --danger:  #ef7c74;  --danger-soft: rgba(239,124,116,.13);
  --sh-1: 0 1px 2px rgba(0,0,0,.5);
  --sh-2: 0 1px 1px rgba(0,0,0,.4), 0 6px 16px -6px rgba(0,0,0,.6);
  --sh-3: 0 12px 40px -12px rgba(0,0,0,.8);
}

/* ---------------------------------------------------------------- base */
*, *::before, *::after { box-sizing: border-box; }

/* The UA stylesheet gives [hidden] display:none, but any explicit display rule of ours
   beats it on specificity and the element stays visible. Every JS toggle uses .hidden,
   so this line is load-bearing. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 14.5px/1.6 var(--font);
  -webkit-font-smoothing: antialiased;
  letter-spacing: -.006em;
}

h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.022em; }
p { margin: 0; }
a { color: var(--accent); }

code, .mono, .num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.num { font-variant-numeric: tabular-nums; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-xs); }

.vh {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.label {
  font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--faint);
}

/* ---------------------------------------------------------------- top bar */
.topbar {
  position: sticky; top: 0; z-index: 40;
  height: var(--bar-h);
  display: flex; align-items: center; gap: 26px;
  padding: 0 var(--pad);
  /* Opaque enough that scrolled content is not legible through it - a 20% window made
     the bar read as a rendering glitch on phones. */
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: saturate(1.6) blur(20px);
  -webkit-backdrop-filter: saturate(1.6) blur(20px);
  border-bottom: 1px solid transparent;
}
.topbar.is-stuck { border-bottom-color: var(--line); }

.mark { display: flex; align-items: center; gap: 9px; flex: 0 0 auto;
        text-decoration: none; color: inherit; }
.mark-glyph { color: var(--accent); display: grid; place-items: center; flex: 0 0 auto; }
.mark-text { font-weight: 600; font-size: 14.5px; letter-spacing: -.018em; white-space: nowrap; }

.tabs { display: flex; gap: 1px; align-items: center; min-width: 0; }
.tabs a {
  display: inline-flex; align-items: center; gap: 7px; white-space: nowrap;
  padding: 6px 12px; border-radius: var(--r-full);
  color: var(--muted); text-decoration: none; font-weight: 500; font-size: 13.5px;
  transition: color .14s, background .14s;
}
.tabs a:hover { color: var(--ink); }
.tabs a[aria-current="page"] { color: var(--ink); background: var(--surface-3); font-weight: 550; }

.bar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }

/* The screen title lives in the app bar on phones, the way a native app does, and the
   in-page heading is hidden there instead. */
.bar-title { display: none; font-weight: 620; font-size: 16.5px; letter-spacing: -.022em; }

/* ---------------------------------------------------------------- page */
.page {
  max-width: 1180px; margin: 0 auto;
  padding: 30px var(--pad) calc(90px + var(--tabbar-h) + var(--safe-b));
}
.page-head { margin-bottom: 26px; }
.page-head h1 { font-size: 30px; letter-spacing: -.03em; line-height: 1.15; }
.page-head p { color: var(--muted); margin-top: 7px; font-size: 14.5px; max-width: 60ch; }

/* ---------------------------------------------------------------- surfaces */
.card {
  background: var(--surface);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-2);
  min-width: 0;
}
.card + .card { margin-top: 16px; }
/* The diagnostics strip is tinted and sits flush at the bottom, so the card has to clip
   it to its own corners. */
#result-card { overflow: hidden; }
.card-pad { padding: 18px; }

.split { display: grid; grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr); gap: 20px;
         align-items: start; }
@media (max-width: 980px) { .split { grid-template-columns: minmax(0,1fr); } }
.sticky-col { position: sticky; top: calc(var(--bar-h) + 20px); }
@media (max-width: 980px) { .sticky-col { position: static; } }

.rule { height: 1px; background: var(--line); border: 0; margin: 0; }

.result-col { display: grid; gap: 16px; min-width: 0; align-content: start; }

.detect-chips { padding: 0 22px 18px; }
.detect-chips span { padding: 6px 14px; }

/* ---------------------------------------------------------------- dropzone */
.dropzone {
  display: grid; place-items: center; text-align: center;
  min-height: 392px; padding: 36px 24px;
  border-radius: var(--r); background: var(--surface-2);
  box-shadow: inset 0 0 0 1.5px var(--line);
  color: var(--muted); cursor: pointer;
  transition: box-shadow .16s ease, background .16s ease;
}
.dropzone:hover { box-shadow: inset 0 0 0 1.5px var(--accent); }
.dropzone.is-drag {
  background: var(--accent-soft); box-shadow: inset 0 0 0 2px var(--accent); color: var(--ink);
}
.dz-glyph {
  width: 52px; height: 52px; border-radius: var(--r); display: grid; place-items: center;
  background: var(--surface); color: var(--accent); margin: 0 auto 16px;
  box-shadow: var(--sh-1);
}
.dz-title { color: var(--ink); font-size: 17px; font-weight: 570; letter-spacing: -.02em;
            margin-bottom: 5px; }
.dz-sub { font-size: 13px; color: var(--muted); }
.dz-cta {
  display: none; margin: 18px auto 0; padding: 11px 22px;
  background: var(--accent); color: var(--accent-ink);
  border-radius: var(--r-full); font-size: 14.5px; font-weight: 560;
}
.dz-kbd {
  font-family: var(--mono); font-size: 11px; padding: 1.5px 5px; border-radius: 5px;
  background: var(--surface-2); color: var(--ink-2);
}

.samples { margin-top: 26px; }
.samples .label { display: block; margin-bottom: 11px; }
.sample-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.sample { padding: 0; border: 0; background: none; cursor: pointer; font: inherit;
          display: block; }
.sample img {
  width: 68px; height: 68px; object-fit: cover; border-radius: var(--r-sm);
  display: block; box-shadow: var(--sh-1);
  transition: transform .16s cubic-bezier(.22,1,.36,1), box-shadow .16s;
}
.sample:hover img { transform: translateY(-3px) scale(1.03); box-shadow: var(--sh-2); }
.sample span { display: block; font-size: 11.5px; color: var(--muted); margin-top: 7px; }

/* ---------------------------------------------------------------- stage card
   One container: source switch in the header, media bleeding to the card edges, and
   every control grouped in a footer. Previously these were seven loose elements stacked
   on the page background next to a tidy card, which read as a pile rather than a panel. */
.stage-card { overflow: hidden; display: flex; flex-direction: column; }
.stage-head {
  padding: 14px 14px 0; display: flex; align-items: center;
  justify-content: space-between; gap: 12px;
}
.stage-media { padding: 14px; }
.stage-foot {
  padding: 0 16px 16px; display: flex; flex-direction: column; gap: 12px;
}
/* The two source footers each stack their own rows. Without this they are plain blocks,
   so the gap above only ever applied BETWEEN the groups - inside them Classify and the
   export strip sat flush against each other with nothing but a border radius between. */
#foot-upload, #foot-camera { display: flex; flex-direction: column; gap: 12px; }
/* An empty error slot is still a flex item, and a flex gap does not know that: it was
   adding a row of dead space under the footer on every screen where nothing went wrong. */
#err-slot:empty, #cam-error:empty { display: none; }

/* Run on the left, the single option that changes what running does on the right, and
   nothing else: the exports below only exist once there is something to export. */
.action-row { display: flex; align-items: stretch; gap: 10px; }
.action-row .btn-primary { flex: 1 1 auto; min-width: 158px; min-height: 44px; }
.action-row .toggle { flex: 0 0 auto; }

/* ---------------------------------------------------------------- source switch */
.source-switch {
  display: inline-flex; padding: 3px; gap: 2px;
  background: var(--surface-2); border-radius: var(--r-full);
}
.source-switch button {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 15px; border: 0; border-radius: var(--r-full); background: none;
  cursor: pointer; font: inherit; font-size: 13px; font-weight: 520; color: var(--muted);
  transition: color .14s, background .14s;
}
.source-switch button:hover { color: var(--ink); }
.source-switch button[aria-pressed="true"] {
  background: var(--raised); color: var(--ink); font-weight: 570; box-shadow: var(--sh-1);
}

/* ---------------------------------------------------------------- camera */
.cam-shell { display: grid; border-radius: var(--r); }
.cam-shell > * { grid-area: 1 / 1; }
#cam-video {
  width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block;
  background: var(--surface-3); border-radius: var(--r);
}
.cam-idle {
  display: grid; place-items: center; text-align: center; padding: 26px;
  background: var(--surface-2); border-radius: var(--r);
}
.cam-row { display: flex; gap: 9px; align-items: stretch; }
.cam-row .btn-block { flex: 1 1 auto; }
.cam-row .btn-quiet { flex: 0 0 auto; }

.shutter {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid currentColor; position: relative; flex: 0 0 auto;
}
.shutter::after {
  content: ""; position: absolute; inset: 3px; border-radius: 50%; background: currentColor;
}

.cam-device-row { gap: 10px; }
.cam-device-row .label { flex: 0 0 auto; }
.select {
  flex: 1 1 auto; min-width: 0;
  padding: 8px 11px; border-radius: var(--r-sm); border: 1px solid var(--line-2);
  background: var(--surface); color: var(--ink); font: inherit; font-size: 13px;
}

/* ---------------------------------------------------------------- stage */
/* NOTE: .stage must NOT set `position`. It shares an element with .sticky-col, and
   because this rule is declared later it would win the cascade at equal specificity -
   turning `position: sticky` into `position: relative`, at which point `top: 80px`
   shifts the whole column down 80px and the sticky behaviour is silently gone. */

.viewer { position: relative; border-radius: var(--r); overflow: hidden;
          background: var(--surface-3); }
.viewer-frame { position: relative; aspect-ratio: 4 / 3; }
.viewer-frame img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: fill; display: block; opacity: 0; transition: opacity .2s ease;
}
.viewer-frame img.on { opacity: 1; }

/* Quiet by default and only coloured on hover: discarding the image is the one destructive
   thing on this screen, so it should be findable without competing with Classify. */
.btn-icon {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  height: 34px; padding: 0 13px; border: 0; border-radius: var(--r-full);
  background: none; color: var(--muted); font: inherit; font-size: 13px; font-weight: 520;
  cursor: pointer; white-space: nowrap;
  transition: background .14s, color .14s;
}
.btn-icon svg { flex: 0 0 auto; opacity: .8; }
.btn-icon:hover { background: var(--danger-soft); color: var(--danger); }
.btn-icon:hover svg { opacity: 1; }

/* The anchor for the floating view switcher. It sits on #viewer rather than .viewer
   because .viewer clips to its own rounded corners - a child positioned against it cannot
   be moved out of the frame later. */
#viewer { position: relative; }

/* On a wide screen the view switcher floats over the image, the way a photo tool does,
   instead of taking its own row in the control stack. Dark glass so it holds up over any
   photograph. On a phone it stays in the flow instead - see the phone block below. */
.seg-float {
  position: absolute; left: 0; right: 0; bottom: 10px; z-index: 3;
  display: flex; justify-content: center; pointer-events: none;
}
.seg-float .seg {
  pointer-events: auto; max-width: calc(100% - 20px);
  background: rgba(12, 12, 11, .58); padding: 3px;
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  box-shadow: 0 4px 16px -4px rgba(0,0,0,.45);
}
.seg-float .seg button { color: rgba(255,255,255,.72); }
.seg-float .seg button:hover:not(:disabled) { color: #fff; }
.seg-float .seg button[aria-pressed="true"] {
  background: rgba(255,255,255,.95); color: #171615; box-shadow: none;
}
.seg-float .seg button:disabled { opacity: .4; }

.seg {
  display: inline-flex; padding: 3px; gap: 2px; align-self: flex-start;
  background: var(--surface-2); border-radius: var(--r-full);
}
.seg button {
  padding: 6px 14px; border: 0; border-radius: var(--r-full); background: none;
  cursor: pointer; font: inherit; font-size: 13px; font-weight: 500; color: var(--muted);
  transition: color .14s, background .14s;
}
.seg button:hover:not(:disabled) { color: var(--ink); }
.seg button[aria-pressed="true"] {
  background: var(--raised); color: var(--ink); font-weight: 560; box-shadow: var(--sh-1);
}
.seg button:disabled { opacity: .35; cursor: not-allowed; }

/* ---------------------------------------------------------------- buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 11px 18px; border-radius: var(--r-full); border: 0;
  font: inherit; font-size: 14px; font-weight: 560; cursor: pointer;
  letter-spacing: -.01em;
  transition: background .14s, color .14s, transform .1s;
}
.btn:active:not(:disabled) { transform: translateY(1px) scale(.995); }

/* The primary action is the only element on the page allowed any depth: a hairline of
   light along the top edge and a shadow tinted with its own colour, so it sits above the
   card instead of being a flat green rectangle painted on it. */
.btn-primary {
  background: var(--accent); color: var(--accent-ink);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 30%, transparent),
              0 6px 16px -8px color-mix(in srgb, var(--accent) 55%, transparent),
              inset 0 1px 0 rgba(255,255,255,.14);
  transition: background .14s, color .14s, transform .1s, box-shadow .16s;
}
.btn-primary:hover:not(:disabled) {
  background: var(--accent-2);
  box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 34%, transparent),
              0 10px 22px -10px color-mix(in srgb, var(--accent) 70%, transparent),
              inset 0 1px 0 rgba(255,255,255,.16);
}
.btn-primary:active:not(:disabled) {
  box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 30%, transparent),
              inset 0 1px 0 rgba(255,255,255,.1);
}
.btn-quiet { background: var(--surface-2); color: var(--ink-2); }
.btn-quiet:hover:not(:disabled) { background: var(--surface-3); color: var(--ink); }
.btn-ghost { background: none; color: var(--muted); padding: 7px 11px; font-size: 12.5px; }
.btn-ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
/* Disabled loses the depth as well as the saturation - a flat shape reads as inert in a
   way that opacity alone does not. */
.btn:disabled { opacity: .38; cursor: not-allowed; box-shadow: none; }
.btn-block { width: 100%; }

.check {
  display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--ink-2);
  cursor: pointer; user-select: none;
}
.check input { accent-color: var(--accent); width: 16px; height: 16px; margin: 0; }

/* An option, not a checkbox. The pill carries its own state, so the action row reads as
   two controls of comparable weight rather than a button with a stray tick beside it. */
.toggle { cursor: pointer; user-select: none; }
.toggle > span {
  display: inline-flex; align-items: center; gap: 7px;
  height: 44px; padding: 0 17px; border-radius: var(--r-full);
  background: var(--surface-2); color: var(--muted);
  font-size: 13.5px; font-weight: 540; white-space: nowrap;
  transition: background .15s, color .15s, box-shadow .15s;
}
.toggle > span svg { opacity: .8; transition: opacity .15s; }
.toggle:hover > span { background: var(--surface-3); color: var(--ink-2); }
.toggle input:checked + span {
  background: var(--accent-soft); color: var(--accent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 26%, transparent);
}
.toggle input:checked + span svg { opacity: 1; }
.toggle input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Exports as one segmented strip rather than four loose ghost buttons, and a pill like the
   source switch above it and the view segment over the image - grouped actions look the
   same everywhere on this card, so the footer reads as part of it rather than a tray. */
.tools {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px;
  padding: 3px; background: var(--surface-2); border-radius: var(--r-full);
  animation: rise-in .22s cubic-bezier(.22,1,.36,1) both;
}
.tool {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 38px; padding: 0 6px; border: 0; border-radius: var(--r-full);
  background: none; color: var(--ink-2); font: inherit; font-size: 12.5px; font-weight: 530;
  cursor: pointer; white-space: nowrap;
  transition: background .15s, color .15s, box-shadow .15s, transform .1s;
}
.tool svg { flex: 0 0 auto; opacity: .65; transition: opacity .15s; }
.tool:hover { background: var(--raised); color: var(--ink); box-shadow: var(--sh-1); }
.tool:hover svg { opacity: 1; }
.tool:active { transform: translateY(1px); }
.tool:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* The strip does not exist until there is a result, so it arrives rather than appears. */
@keyframes rise-in { from { opacity: 0; transform: translateY(5px); } }

/* ---------------------------------------------------------------- verdict */
.verdict { padding: 24px 22px 20px; }
.verdict-name {
  font-size: 34px; font-weight: 640; letter-spacing: -.035em; line-height: 1.08;
  margin-bottom: 12px; overflow-wrap: break-word;
}
.verdict-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.verdict-pct { font-size: 21px; font-weight: 620; letter-spacing: -.03em;
               font-variant-numeric: tabular-nums; color: var(--accent); }
.verdict-note { font-size: 12.5px; color: var(--faint); font-variant-numeric: tabular-nums; }

.pill {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 3px 10px; border-radius: var(--r-full);
  font-size: 11.5px; font-weight: 600; letter-spacing: .01em;
  background: var(--accent-soft); color: var(--accent);
}

.band-high      { --accent: var(--ok);     --accent-soft: var(--ok-soft); }
.band-medium    { --accent: var(--info);   --accent-soft: var(--info-soft); }
.band-uncertain { --accent: var(--warn);   --accent-soft: var(--warn-soft); }
.band-low       { --accent: var(--danger); --accent-soft: var(--danger-soft); }

/* ---------------------------------------------------------------- probabilities */
.probs { padding: 4px 22px 18px; display: flex; flex-direction: column; gap: 13px; }
.prob { display: grid; grid-template-columns: 1fr auto; gap: 6px 12px; }
.prob-name { font-size: 13.5px; color: var(--muted); }
.prob-val { font-size: 13px; color: var(--faint); font-variant-numeric: tabular-nums; }
.prob-track {
  grid-column: 1 / -1; height: 3px; border-radius: var(--r-full);
  background: var(--surface-3); overflow: hidden;
}
.prob-fill {
  height: 100%; border-radius: var(--r-full); background: var(--line-2);
  transition: width .5s cubic-bezier(.22,1,.36,1);
}
.prob.is-top .prob-name { color: var(--ink); font-weight: 560; font-size: 14px; }
.prob.is-top .prob-val { color: var(--ink); font-weight: 560; }
.prob.is-top .prob-track { height: 5px; }
.prob.is-top .prob-fill { background: var(--accent); }

.section-split { height: 1px; background: var(--line); margin: 0; border: 0; }

/* The card's last strip. A half-step of surface tone puts the diagnostics behind the
   result rather than beside it, without another rule across the card. */
.meta-line {
  padding: 12px 22px; font-size: 11.5px; color: var(--faint);
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  display: flex; gap: 0 10px; flex-wrap: wrap;
  background: var(--surface-2); border-top: 1px solid var(--line);
}
.meta-line span + span::before { content: "·"; margin-right: 10px; color: var(--line-2); }

/* ---------------------------------------------------------------- explain */
.explain-head { display: flex; align-items: baseline; gap: 10px; padding: 18px 22px 12px; }
.explain-head .label:first-child { margin-right: auto; }
.explain-head .label + .label { text-transform: none; letter-spacing: 0;
                               font-weight: 500; color: var(--muted); }
.class-grid { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 7px;
              padding: 0 22px 16px; }
@media (max-width: 460px) { .class-grid { grid-template-columns: repeat(2, minmax(0,1fr)); } }
.class-grid button {
  padding: 9px 10px; border-radius: var(--r-full); border: 0;
  background: var(--surface-2); color: var(--ink-2); font: inherit; font-size: 12.5px;
  cursor: pointer; text-align: center; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap;
  transition: background .14s, color .14s, box-shadow .14s, transform .1s;
}
.class-grid button:hover { background: var(--surface-3); color: var(--ink); }
.class-grid button:active { transform: translateY(1px); }
.class-grid button[aria-pressed="true"] {
  background: var(--accent); color: var(--accent-ink); font-weight: 560;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--accent) 28%, transparent),
              inset 0 1px 0 rgba(255,255,255,.14);
}

.footnote { padding: 0 22px 18px; font-size: 11.5px; color: var(--faint); line-height: 1.55; }

/* ---------------------------------------------------------------- empty */
.empty { display: grid; place-items: center; text-align: center; padding: 60px 26px;
         color: var(--faint); }
.empty-glyph { color: var(--line-2); margin-bottom: 14px; }
.empty p { font-size: 13.5px; max-width: 30ch; }

/* ---------------------------------------------------------------- notices */
.notice {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 14px; border-radius: var(--r); font-size: 13px; line-height: 1.5;
}
.notice + .notice { margin-top: 8px; }
.notice svg { flex: 0 0 auto; margin-top: 1.5px; }
.notice strong { display: block; font-weight: 600; }
.notice-warn  { background: var(--warn-soft);   color: var(--warn); }
.notice-error { background: var(--danger-soft); color: var(--danger); }
.notice-info  { background: var(--info-soft);   color: var(--info); }
.notice-ok    { background: var(--ok-soft);     color: var(--ok); }
.notice-stack { padding: 0 22px 14px; }

/* ---------------------------------------------------------------- session */
.hist { display: flex; flex-direction: column; }
.hist-row { display: flex; align-items: center; gap: 12px; padding: 10px 22px; }
.hist-row + .hist-row { border-top: 1px solid var(--line); }
.hist-row img { width: 38px; height: 38px; border-radius: var(--r-xs); object-fit: cover;
                flex: 0 0 auto; }
.hist-main { min-width: 0; flex: 1 1 auto; }
.hist-name { font-size: 13.5px; font-weight: 520; overflow: hidden; text-overflow: ellipsis;
             white-space: nowrap; }
.hist-meta { font-size: 11.5px; color: var(--faint); font-variant-numeric: tabular-nums; }
.hist-conf { font-size: 13px; font-weight: 560; font-variant-numeric: tabular-nums;
             flex: 0 0 auto; color: var(--muted); }

/* ---------------------------------------------------------------- data tables */
.kv { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.kv td { padding: 11px 0; vertical-align: top; }
.kv tr + tr td { border-top: 1px solid var(--line); }
.kv td:first-child { color: var(--muted); width: 44%; padding-right: 14px; }
.kv td:last-child { color: var(--ink-2); }

.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chips span {
  display: inline-flex; align-items: baseline; gap: 8px;
  background: var(--surface-2); border-radius: var(--r-full); padding: 5px 13px;
  font-size: 13px;
}
.chips i { font-style: normal; font-family: var(--mono); font-size: 11px; color: var(--faint); }

.cols-3 { display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 16px;
          align-items: start; }
@media (max-width: 1040px) { .cols-3 { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 700px)  { .cols-3 { grid-template-columns: minmax(0,1fr); } }

.prose { font-size: 13.5px; color: var(--ink-2); line-height: 1.68; }
.prose ul { margin: 10px 0 0; padding-left: 20px; }
.prose li { margin-bottom: 6px; }

/* ---------------------------------------------------------------- theme button */
.theme-btn {
  width: 34px; height: 34px; display: grid; place-items: center;
  border: 0; border-radius: var(--r-full); background: var(--surface-2);
  color: var(--ink-2); cursor: pointer; padding: 0;
  transition: background .14s, color .14s;
}
.theme-btn:hover { background: var(--surface-3); color: var(--ink); }
.theme-btn svg { grid-area: 1 / 1; transition: opacity .18s, transform .25s cubic-bezier(.22,1,.36,1); }
/* Show the icon for the mode you would switch TO. */
.theme-btn .i-moon { opacity: 1; transform: rotate(0) scale(1); }
.theme-btn .i-sun  { opacity: 0; transform: rotate(-90deg) scale(.6); }
:root[data-theme="dark"] .theme-btn .i-moon { opacity: 0; transform: rotate(90deg) scale(.6); }
:root[data-theme="dark"] .theme-btn .i-sun  { opacity: 1; transform: rotate(0) scale(1); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-btn .i-moon { opacity: 0; transform: rotate(90deg) scale(.6); }
  :root:not([data-theme="light"]) .theme-btn .i-sun  { opacity: 1; transform: rotate(0) scale(1); }
}

/* ---------------------------------------------------------------- spinner/toast */
.spin { width: 14px; height: 14px; border-radius: 50%;
        border: 2px solid currentColor; border-right-color: transparent;
        animation: spin .62s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.is-busy { position: relative; }
.is-busy::after {
  content: ""; position: absolute; inset: 0; border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--surface) 45%, transparent);
  backdrop-filter: blur(2px);
}

.toast {
  position: fixed; left: 50%; transform: translateX(-50%) translateY(10px);
  bottom: calc(22px + var(--tabbar-h) + var(--safe-b));
  z-index: 90; padding: 10px 17px; border-radius: var(--r-full);
  background: var(--ink); color: var(--bg); font-size: 13px; font-weight: 520;
  box-shadow: var(--sh-3); opacity: 0; pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---------------------------------------------------------------- compare */
.compare { position: absolute; inset: 0; display: none; }
.viewer-frame.mode-compare .compare { display: block; }
.viewer-frame.mode-compare img.on { opacity: 0; }
.compare-clip { position: absolute; inset: 0; overflow: hidden; width: 50%; }
.compare-base { position: absolute; inset: 0; }

/* `fill`, not `contain`. The overlay is the 224x224 render and the source photo has its
   own aspect ratio; contain would letterbox one side and not the other, so the leaf would
   not line up across the divider and the comparison would be meaningless. */
.compare-clip img,
.compare-base img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: fill; opacity: 1;
}
.compare-handle {
  position: absolute; top: 0; bottom: 0; width: 2px; background: rgba(255,255,255,.9);
  cursor: ew-resize; touch-action: none;
}
.compare-handle::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 36px; height: 36px; transform: translate(-50%, -50%);
  border-radius: 50%; background: #fff; box-shadow: 0 2px 10px rgba(0,0,0,.3);
}
.compare-handle::before {
  content: ""; position: absolute; top: 50%; left: 50%; z-index: 1;
  width: 13px; height: 9px; transform: translate(-50%, -50%);
  background: #171615;
  clip-path: polygon(0 50%, 38% 0, 38% 100%, 62% 0, 62% 100%, 100% 50%);
}
.compare-tag {
  position: absolute; top: 12px; padding: 3px 9px; border-radius: var(--r-full);
  background: rgba(10,10,9,.55); color: #fff; font-size: 10.5px; font-weight: 600;
  letter-spacing: .03em; pointer-events: none;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.compare-tag.l { left: 12px; }
.compare-tag.r { right: 12px; }

/* scrollIntoView does not know about position:sticky, so without this the auto-scroll
   on phones parks the verdict underneath the top bar. */
.card, #result { scroll-margin-top: calc(var(--bar-h) + 16px); }

/* ---------------------------------------------------------------- bottom tab bar */
.tabbar { display: none; }

@media (max-width: 980px) {
  :root { --tabbar-h: 62px; --pad: 16px; }

  .topbar .tabs { display: none; }
  .topbar { gap: 12px; }
  .topbar .mark-text { display: none; }
  .bar-title { display: block; }

  .tabbar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
    height: calc(var(--tabbar-h) + var(--safe-b));
    padding-bottom: var(--safe-b);
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    backdrop-filter: saturate(1.6) blur(20px);
    -webkit-backdrop-filter: saturate(1.6) blur(20px);
    border-top: 1px solid var(--line);
  }
  .tabbar a {
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px; text-decoration: none; color: var(--faint);
    font-size: 10.5px; font-weight: 520; position: relative;
  }
  .tabbar a svg { position: relative; z-index: 1; }
  .tabbar a::before {
    content: ""; position: absolute; top: 6px; width: 46px; height: 26px;
    border-radius: var(--r-full); background: transparent;
    transition: background .16s ease;
  }
  .tabbar a[aria-current="page"] { color: var(--accent); font-weight: 580; }
  .tabbar a[aria-current="page"]::before { background: var(--accent-soft); }
}

/* A keyboard paste hint is noise on a touch device, and a large dashed area is a weaker
   affordance than a button - so phones get an explicit call to action instead. */
@media (pointer: coarse) {
  .dz-paste { display: none; }
  .dz-browse { display: none; }
  .dz-cta { display: inline-block; }
}

/* ---------------------------------------------------------------- phone tuning */
@media (max-width: 980px) {
  /* App-shaped: the screen title is in the bar, so the in-page heading goes away and
     content starts immediately - a marketing-style page head reads as a website. */
  .page { padding-top: 14px; }
  .page-head { display: none; }

  .card { border-radius: var(--r-lg); }
  .split { gap: 14px; }

  /* The primary action used to be position:sticky here, to keep it in thumb reach while a
     tall image scrolled past. It had to go. A sticky element travels only within its
     containing block, and its containing block is .action-row - which, since the Explain
     pill moved above it, means the button slides up and lands exactly on top of the pill.
     In the empty state the row sits below the fold, so the overlap was total: two labels
     printed over each other.
     It was also buying nothing. The card fits a phone screen without scrolling, and after
     a result you scroll down to read the verdict, not back up to press Classify again. */
  .check { justify-content: center; }

  .dropzone { min-height: 296px; padding: 30px 16px; }
  .stage-head { padding: 12px 12px 0; gap: 8px; }
  .stage-media { padding: 12px; }
  .stage-foot { padding: 0 14px 14px; }
  /* The option sits above the action so the sticky Classify button is the last thing in
     the group and nothing scrolls out from under it. */
  .action-row { gap: 10px; flex-wrap: wrap; }
  .action-row .btn-primary { flex: 1 1 100%; }
  .action-row .toggle { order: -1; flex: 1 1 100%; margin-left: 0; }
  .action-row .toggle > span { width: 100%; justify-content: center; height: var(--tap); }
  .detect-chips { padding: 0 18px 16px; }
  /* The switcher comes off the photograph. On a 390px screen the image is small enough
     that a floating bar covers the part of the leaf you are trying to look at - and the
     heatmap's hottest region is often exactly there, at the centre-bottom. In the flow it
     costs one row and hides nothing.
     It also stops being dark glass, because it is no longer sitting on a photo: these
     rules put it back to the same light track the source switch uses. Each needs the
     .seg-float prefix to outrank the desktop rules above, which are more specific than
     the plain .seg rules that would otherwise apply. */
  .seg-float { position: static; margin-top: 10px; }
  .seg-float .seg {
    max-width: none; width: 100%;
    background: var(--surface-2);
    backdrop-filter: none; -webkit-backdrop-filter: none;
    box-shadow: none;
  }
  .seg-float .seg button { color: var(--muted); }
  .seg-float .seg button:disabled { opacity: .35; }
  .seg-float .seg button[aria-pressed="true"] {
    background: var(--raised); color: var(--ink); box-shadow: var(--sh-1);
  }
  .source-switch { width: 100%; }
  .source-switch button { flex: 1 1 0; justify-content: center; min-height: var(--tap); }
  #cam-video { aspect-ratio: 3 / 4; }        /* portrait framing on a phone */
  .cam-row .btn { min-height: var(--tap); }

  .seg { width: 100%; padding: 4px; }
  .seg button { flex: 1 1 0; min-height: var(--tap); padding: 0 6px; font-size: 12.5px;
                font-weight: 540; }
  .btn { min-height: var(--tap); }
  .btn-ghost { min-height: 38px; }
  .class-grid button { min-height: var(--tap); }
  .check { min-height: var(--tap); }
  .check input { width: 18px; height: 18px; }
  .theme-btn { width: 42px; height: 42px; }
  .btn-icon { height: 38px; padding: 0 12px; }
  .source-switch { min-width: 0; }

  .verdict { padding: 22px 18px 18px; }
  .verdict-name { font-size: 29px; letter-spacing: -.04em; }
  .verdict-pct { font-size: 23px; }
  .probs { padding: 4px 18px 16px; }
  .meta-line { padding: 12px 18px; }
  .explain-head, .class-grid, .footnote, .notice-stack { padding-left: 18px; padding-right: 18px; }
  /* Four labels will not fit side by side at 360px, so the icon moves above the word
     instead of the word being dropped - an unlabelled icon row is a guessing game. */
  .tools { gap: 3px; padding: 4px; }
  .tool { flex-direction: column; gap: 5px; min-height: 54px; padding: 8px 2px;
          font-size: 10.5px; letter-spacing: -.005em; }
  .hist-row { padding: 10px 18px; }
  .card-pad { padding: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
