/* PoolCoach Analyzer front-end styles, mobile first.
   No framework, no build step.

   Some rules below still style parts of the earlier app (the suggestion
   tab, the drill tab, the image-scan overlay) that this repository does not
   ship. They are kept for now because several Analyzer elements inherit
   from the same base rules; removing them is a separate cleanup that needs
   its own visual check. */

:root {
  --bg: #14181d;
  --card: #1e242b;
  --card-b: #2d3640;
  --text: #e9edf1;
  --muted: #93a0ac;
  --green: #0da357;
  --green-d: #0b8a4b;
  --gold: #ffd700;
  --zone: #38c8f5;   /* the drill target zone -- deliberately NOT the gold
                        of a target pocket */
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: system-ui, "Segoe UI", Roboto, sans-serif;
  background:
    radial-gradient(1200px 600px at 20% -10%, #1d242c 0%, var(--bg) 55%);
  color: var(--text);
  -webkit-tap-highlight-color: transparent;
}

header { padding: 16px 18px 6px; }
header h1 { margin: 0; font-size: 22px; letter-spacing: .3px; }
header p { margin: 4px 0 0; color: var(--muted); font-size: 13px; }

main {
  display: flex;
  gap: 20px;
  padding: 12px 18px 26px;
  align-items: flex-start;
  flex-wrap: wrap;
  justify-content: center;
}

/* ----------------------------------------------------------- the table */
#table-wrap { position: relative; flex: 0 0 auto; }

#table {
  display: block;
  border-radius: 16px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, .5);
  cursor: grab;
  touch-action: none;      /* dragging must not scroll the page on mobile */
  user-select: none;
}


/* ----------------------------------------------------------- panel */
#panel { flex: 1 1 320px; min-width: 300px; max-width: 440px; }

.banner {
  background: #453d10;
  border: 1px solid #8a7418;
  color: #ffe58f;
  padding: 10px 13px;
  border-radius: 10px;
  font-size: 13px;
  margin-bottom: 12px;
}
.banner.error { background: #4a1d1d; border-color: #a33; color: #ffb3b3; }
.banner.warn  { background: #4a3a10; border-color: #b8901f; color: #ffdf9e; }
.banner.win {
  background: #12321f;
  border-color: var(--green);
  color: #9df2c0;
  font-size: 15px;
  font-weight: 650;
  text-align: center;
}


/* .an-cta is a call-to-action button that LOOKS like the old .btn-go but
   is its own class. In the earlier app, app.js bound an action to every
   .btn-go and forced their label and disabled state, so any button
   borrowing that class inherited both -- a real bug caught in a smoke test.
   The rule stands as a reminder: never borrow a class that carries an event
   binding. */
.btn-go, .an-cta {
  width: 100%;
  padding: 13px;
  font-size: 15px;
  font-weight: 650;
  color: #fff;
  background: linear-gradient(180deg, var(--green), var(--green-d));
  border: 0;
  border-radius: 12px;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(13, 163, 87, .25);
  transition: filter .15s, transform .05s;
}
.btn-go:hover:not(:disabled), .an-cta:hover:not(:disabled) { filter: brightness(1.08); }
.btn-go:active:not(:disabled), .an-cta:active:not(:disabled) { transform: translateY(1px); }
.btn-go:disabled, .an-cta:disabled {
  background: #333c45;
  color: #8a949d;
  cursor: not-allowed;
  box-shadow: none;
}

/* ---------------------------------------- secondary buttons */
.btn-row { display: flex; gap: 8px; margin-bottom: 12px; }

.btn-mini {
  flex: 1;
  padding: 9px 12px;
  font-size: 13px;
  color: var(--text);
  background: #232a32;
  border: 1px solid var(--card-b);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color .15s, background .15s;
}
.btn-mini:hover:not(:disabled) { border-color: #55636f; background: #283039; }
.btn-mini:disabled { opacity: .45; cursor: not-allowed; }

/* the cue-ball and power-bar widget of the earlier app */
.viz {
  display: flex;
  gap: 18px;
  align-items: center;
  background: var(--card);
  border: 1px solid var(--card-b);
  border-radius: 14px;
  padding: 13px 15px;
  margin-bottom: 10px;
}
.viz-spin { flex: 0 0 auto; text-align: center; }
.viz-cap { color: var(--muted); font-size: 11.5px; margin-top: 5px; }
/* The width cap belongs to the caption UNDER THE BALL, which is tied to
   that column's width -- it used to sit on .viz-cap itself, so the caption
   in the wide right-hand column was squeezed to 120px too and broke one
   sentence over five cramped lines. */
.viz-spin .viz-cap {
  max-width: 140px;
  text-wrap: balance;      /* two tidy halves instead of a stray last word */
}
.viz-power .viz-cap { text-wrap: pretty; }
.viz-power { flex: 1; min-width: 0; }
.viz-lbl { font-size: 13px; color: var(--muted); margin-bottom: 8px; }
.viz-lbl b { color: var(--text); font-size: 14px; margin-left: 4px; }
.viz-scale {
  display: flex;
  justify-content: space-between;
  color: var(--muted);
  font-size: 10.5px;
  margin-top: 5px;
}
/* The replay widget on the detail page: it looks EXACTLY like the older
   #shotviz but carries its OWN interactive classes, because .power-bar and
   .spin-dot were swept globally by the suggestion tab's initialisers. */
#and-spin-ball { width: 104px; height: 104px; display: block; margin: 0 auto; }
.and-spin-dot { transition: cx .25s ease, cy .25s ease; }
.and-power-bar { display: flex; gap: 4px; }
.and-power-bar .seg {
  flex: 1;
  height: 17px;
  border-radius: 4px;
  background: #2a323b;
  transition: background .2s;
}
#and-shotviz { margin-top: 10px; }
/* The hero at the top of the detail page: the spin label is the headline of
   the whole page, so it reads at heading size with the plain sentence and
   the power line stepping down from it. */
.and-hero-head {
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
}
.and-hero-spin {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: .2px;
}
.and-hero-sub {
  color: var(--muted);
  font-size: 12.5px;
  margin: 3px 0 10px;
}
.and-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0 2px;
  font-size: 13px;
  cursor: pointer;
}
/* Progressive disclosure: the technical numbers are one click away, not
   gone. It reads as a quiet drawer rather than a section heading, so the
   hero above it stays the loudest thing on the page. */
.and-tech {
  margin-top: 12px;
  border: 1px solid var(--card-b);
  border-radius: 10px;
  padding: 8px 11px;
}
.and-tech > summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  list-style: none;
}
.and-tech > summary::-webkit-details-marker { display: none; }
/* The caret is DRAWN out of borders, not typed: a decorative glyph in
   `content` was mangled into a control byte on the way to disk once
   already (a unicode escape for the triangle reached disk as 0x15
   followed by "B8") and showed on screen as a broken box. No glyph,
   no font dependency, no escape left to get wrong -- just a triangle
   that turns. Keep it so: no decorative unicode in CSS `content`. */
.and-tech > summary::before {
  content: "";
  display: inline-block;
  width: 0;
  height: 0;
  margin-right: 9px;
  vertical-align: middle;
  border-left: 6px solid currentColor;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform .18s ease;
}
.and-tech[open] > summary::before { transform: rotate(90deg); }
.and-tech[open] > summary { margin-bottom: 4px; }
.and-tech-links { margin-top: 10px; }


/* ------------------------------------------- the full-screen overlay */
/* Every .scan-* class was introduced fresh, borrowing nothing that carries
   an event binding. The overlay floats above everything (including the
   mobile sheet at z-index 20 and the backdrop at 15), because placing
   corners is a full-screen job. The Analyzer's corner overlay reuses these
   STYLE-ONLY classes. */
.scan-overlay {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: rgba(0, 0, 0, .72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px;
}
.scan-box {
  background: var(--card);
  border: 1px solid var(--card-b);
  border-radius: 16px;
  padding: 13px 15px;
  max-width: min(94vw, 760px);
  max-height: 94vh;
  overflow-y: auto;
  box-shadow: 0 12px 44px rgba(0, 0, 0, .6);
}
.scan-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
  font-size: 15px;
}
.scan-x {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: #232a32;
  border: 1px solid var(--card-b);
  color: var(--text);
  font-size: 14px;
  cursor: pointer;
}
.scan-hint {
  color: #ffdf9e;
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: 10px;
  min-height: 2.9em;   /* two lines, so a longer hint does not shift the
                          image */
}
.scan-actions { margin-top: 12px; margin-bottom: 0; }
.scan-go { flex: 2; width: auto; padding: 11px; }

/* ------------------------------------------------- the Analyzer view */
/* Every .an-* class is new; the pocket overlay borrows the STYLE-ONLY
   .scan-overlay/.scan-box classes, none of which carry an event binding. */
.an-intro {
  font-size: 13px;
  color: var(--muted, #9fb0c0);
  line-height: 1.5;
  margin-bottom: 10px;
}
/* One declaration, not two. There used to be a second `.an-note` block
   further down that overrode only the type, so `display: flex` from up here
   leaked into EVERY note on the page and laid its prose out as flex items.
   The flex layout belongs to the illustrated note alone, which now says so
   with a class of its own. */
.an-note {
  margin: 8px 0 10px;
  font-size: 12px;
  color: var(--muted, #9fb0c0);
  line-height: 1.5;
}
/* the static note stating the cloth-nose click standard, above the corner
   canvas: a cross-section drawing beside its sentence */
.an-note-illus {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  line-height: 1.45;
}
.an-note-illus svg {
  width: 168px;
  flex-shrink: 0;
}
#an-canvas {
  display: block;
  max-width: 100%;
  max-height: 62vh;
  margin: 0 auto;
  border-radius: 10px;
  border: 1px solid var(--card-b);
  cursor: crosshair;
  touch-action: none;
}
.an-metrics {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  margin-top: 8px;
}
.an-metrics td {
  padding: 5px 6px;
  border-bottom: 1px solid var(--card-b);
}
.an-metrics td:last-child {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.an-warn {
  background: #4a3a10;
  border: 1px solid #b8901f;
  color: #ffdf9e;
  border-radius: 8px;
  padding: 7px 10px;
  font-size: 12.5px;
  line-height: 1.45;
  margin-bottom: 6px;
}
/* The two-column model-versus-analytic table: the header and the middle
   value cell (the model column) use the same numeric style as the last
   column. The model caveat and the disagreement lines are INFORMATION, so
   they read in a different tone from the .an-warn warnings. */
.an-metrics th {
  padding: 5px 6px;
  border-bottom: 1px solid var(--card-b);
  text-align: left;
  font-size: 12px;
  color: var(--muted, #9fb0c0);
}
.an-metrics th:not(:first-child) { text-align: right; }
.an-metrics td:not(:first-child) {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.an-caveat {
  margin-top: 8px;
  background: #16324a;
  border: 1px solid #2f6da8;
  color: #bcdcf5;
  border-radius: 8px;
  padding: 7px 10px;
  font-size: 12.5px;
  line-height: 1.45;
}
.an-diff {
  background: #14323e;
  border: 1px solid #2e7f9e;
  color: #a9e0f2;
  border-radius: 8px;
  padding: 7px 10px;
  font-size: 12.5px;
  line-height: 1.45;
  margin-bottom: 6px;
}

/* ------------------------------------------------- the shot list table.
   Every .anv-* class is new, borrowing nothing that carries an event
   binding. An .anv-err row is a shot that could not be analysed: greyed,
   with its reason. */
#anv-result { margin-top: 12px; }
.anv-progressline {
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 8px;
}
.anv-tablewrap { overflow-x: auto; }
.anv-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
  white-space: nowrap;
}
.anv-table th {
  padding: 5px 6px;
  border-bottom: 1px solid var(--card-b);
  text-align: left;
  font-size: 12px;
  color: var(--muted, #9fb0c0);
}
.anv-table td {
  padding: 5px 6px;
  border-bottom: 1px solid var(--card-b);
  vertical-align: middle;
  font-variant-numeric: tabular-nums;
}
.anv-thumb {
  display: block;
  width: 72px;
  border-radius: 4px;
  border: 1px solid var(--card-b);
}
.anv-ts {
  font-size: 11.5px;
  color: var(--muted, #9fb0c0);
  text-align: center;
}
.anv-badge {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
}
/* The spin cell: a picture of WHERE the cue hit, the class in words, and
   how much to trust it. The dot carries .anv-spin-dot and NOT
   .and-spin-dot -- fillAndShotviz sweeps every .and-spin-dot on the page
   from the shot currently open, which would rewrite every row at once. */
.anv-spin-cell { display: flex; align-items: center; gap: 9px; }
.anv-spin-ball {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  display: block;
}
.anv-spin-lbl { font-size: 12.5px; margin-bottom: 3px; }
.anv-badge-sm { padding: 0 7px; font-size: 10.5px; }
/* The power cell: the same 0.5-8.0 m/s scale as the detail widget, at row
   size, with its own class for the same reason as the spin dot. */
.anv-power-cell { min-width: 104px; }
.anv-power-bar { display: flex; gap: 2px; }
.anv-power-bar .seg {
  flex: 1;
  height: 9px;
  border-radius: 2px;
  background: #2a323b;
}
.anv-power-lbl { font-size: 12px; margin-top: 5px; }
.anv-badge-high { background: #143f28; border: 1px solid #2e9e56; color: #8fe0ae; }
.anv-badge-medium { background: #4a3a10; border: 1px solid #b8901f; color: #ffdf9e; }
.anv-badge-low { background: #4a1d15; border: 1px solid #b8502f; color: #ffb59e; }
.anv-err td { color: var(--muted, #9fb0c0); background: rgba(255, 255, 255, .025); }
/* The valve: a shot suspected of not being a shot. Greyed like .anv-err
   but its OWN class, because the row is still done and still clickable
   whereas .anv-err means the shot could not be analysed at all. Never
   borrow a class whose meaning differs. */
.anv-suspect td { color: var(--muted, #9fb0c0); background: rgba(255, 255, 255, .025); }
.anv-reason {
  font-size: 11.5px;
  color: var(--muted, #9fb0c0);
  white-space: normal;
  max-width: 260px;
}
.anv-clickable { cursor: pointer; }
.anv-clickable:hover td { background: rgba(255, 255, 255, .05); }
/* the per-shot overlay: play and download links inside the status cell */
.anv-overlay-links a {
  display: inline-block;
  margin: 2px 10px 0 0;
  font-size: 11.5px;
  color: #7fc4ff;
  text-decoration: none;
}
.anv-overlay-links a:hover { text-decoration: underline; }

/* ----------------------------- the shot detail page (.and-* classes) */
#and-detail h3 { margin: 8px 0 4px; }
.and-video {
  width: 100%;
  max-height: 320px;
  border-radius: 8px;
  background: #000;
  margin: 6px 0;
}
.and-link {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}
#and-replay { margin: 6px 0; }

/* ------------------------------------------------------------ toast */
#toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  background: #4a1d1d;
  border: 1px solid #a33;
  color: #ffd6d6;
  padding: 11px 16px;
  border-radius: 10px;
  font-size: 14px;
  max-width: 92vw;
  z-index: 10;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .45);
}

.hidden { display: none !important; }

/* the quickbar and the bottom sheet: mobile only */
#quickbar, #backdrop, .sheet-close { display: none; }

/* ----------------------------------------------------------- mobile */
@media (max-width: 760px) {
  header { padding: 10px 14px 0; text-align: center; }
  header h1 { font-size: 18px; }
  header p { display: none; } /* tighter -- the UI explains itself */

  main {
    flex-direction: column;
    align-items: center;
    gap: 12px;
    padding: 8px 12px calc(150px + env(safe-area-inset-bottom)); /* room for the quickbar */
  }

  /* the panel is HIDDEN by default and opens as a bottom sheet from the
     "Details" button */
  #panel { display: none; }

  body.sheet-open #backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .55);
    z-index: 15;
  }
  body.sheet-open #panel {
    display: block;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 20;
    max-width: none;
    max-height: 78vh;
    overflow-y: auto;
    background: var(--bg);
    border-radius: 18px 18px 0 0;
    padding: 12px 14px calc(18px + env(safe-area-inset-bottom));
    box-shadow: 0 -12px 44px rgba(0, 0, 0, .6);
    animation: slideup .22s ease;
  }
  body.sheet-open .sheet-close {
    display: flex;
    align-items: center;
    justify-content: center;
    position: sticky;
    top: 0;
    margin-left: auto;
    width: 36px; height: 36px;
    background: var(--card);
    border: 1px solid var(--card-b);
    border-radius: 50%;
    color: var(--text);
    font-size: 15px;
    cursor: pointer;
    z-index: 2;
  }

  /* the quickbar pinned to the bottom of the screen */
  #quickbar {
    display: flex;
    flex-direction: column;
    gap: 7px;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 9;
    background: var(--card);
    border-top: 1px solid var(--card-b);
    padding: 9px 12px calc(9px + env(safe-area-inset-bottom));
    box-shadow: 0 -6px 24px rgba(0, 0, 0, .4);
  }
  .qb-row { display: flex; align-items: center; gap: 9px; }
  /* the engine pills no longer fitted one row at 375px, so they wrap
     instead of overflowing. Settings row only; the main row stays on one
     line. */
  .qb-row.qb-settings { flex-wrap: wrap; }
  #qb-detail, #qb-drills {
    background: none;
    border: 1px solid var(--card-b);
    color: var(--text);
    border-radius: 999px;
    padding: 6px 13px;
    font-size: 12.5px;
    cursor: pointer;
  }
  /* push the buttons to the right, with the target badge on the left */
  .qb-mid { flex: 1; min-width: 0; }
  .qb-power { gap: 3px; }
  .qb-power .seg { height: 12px; border-radius: 3px; }

  /* the hero gives width back to the words: at 375px the ball column was
     taking 140px of 347 to fit one caption line, which cost the sentence
     beside it two extra lines. The caption wraps to two instead. */
  #and-spin-ball { width: 84px; height: 84px; }
  .viz-spin .viz-cap { max-width: 112px; }
  .viz { gap: 13px; }

  /* the Analyzer row keeps the same height as the row above it, so the
     bottom of the screen does not jump (main already reserves 150px for the
     quickbar) */
  #qban-spin { width: 50px; height: 50px; flex: 0 0 auto; }
  .qban-bar .seg { height: 12px; border-radius: 3px; }
  #qban-info {
    color: var(--muted);
    font-size: 11.5px;
    margin-top: 5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .qban-replay {
    flex: 0 0 auto;
    width: auto;
    padding: 12px 15px;
    font-size: 14px;
    border-radius: 11px;
  }

  #toast { bottom: calc(150px + env(safe-area-inset-bottom)); }

  /* inside the sheet: larger tap targets */
  /* chips: nine buttons wrapping onto two rows in the sheet, with bigger
     tap targets */
}

@keyframes slideup {
  from { transform: translateY(28%); opacity: .4; }
  to   { transform: none; opacity: 1; }
}
