/* Plotly AI Analyst demo, marketing theme */

:root {
  --bg: #0e1220;
  --panel: #161b2e;
  --panel-2: #1c2340;
  --border: rgba(255, 255, 255, 0.08);
  --text: #e8eaf2;
  --muted: #96a0b8;
  --accent: #636efa;
  --accent-2: #7f7fff;
  --danger: #ff6b6b;
  --success: #00cc96;
  --scroll-thumb: rgba(255, 255, 255, 0.16);
  --scroll-thumb-hover: rgba(255, 255, 255, 0.28);

  color-scheme: dark;
}

/* Scrollbars: the default light chrome reads as a bright seam against the
   dark panels, so every scrollable surface gets the dark treatment. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--scroll-thumb) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

html, body {
  margin: 0;
  background:
    radial-gradient(1200px 500px at 85% -10%, rgba(99, 110, 250, 0.25), transparent 60%),
    radial-gradient(900px 500px at -10% 110%, rgba(25, 211, 243, 0.12), transparent 60%),
    var(--bg);
  color: var(--text);
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
}

.page {
  display: flex;
  gap: 0;
  /* Flush to the right and bottom edges; the hero owns the top gap and the
     left keeps its margin so the copy is not against the glass. */
  padding: 0 0 0 24px;
  height: 100vh;
  box-sizing: border-box;
}

.main-col {
  container-type: inline-size;
  container-name: main;
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding-right: 24px;
  padding-bottom: 24px;
}

/* The 24px inset on .main-col is padding, so it renders between the content
   and the scrollbar rather than beyond it.  The chat panel then butts
   straight up against the scrollbar with no margin of its own — the
   separation a viewer sees is the padding, on the content side. */

.analyst-col {
  /* Collapsed until data is registered.  Width is animated rather than
     toggled with `display`, which cannot transition; the component stays
     mounted the whole time so its callbacks and polling keep working. */
  flex: 0 0 0%;
  min-width: 0;
  opacity: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-width: 0;
  /* Docked on the top, right, and bottom edges, so square: any radius
     leaves a notch of page background against the glass. */
  border-radius: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  transition:
    flex-basis 450ms cubic-bezier(0.22, 0.61, 0.36, 1),
    opacity 300ms ease 120ms,
    border-width 450ms step-end;
}

.analyst-col.is-open {
  flex-basis: 50%;
  opacity: 1;
  border-width: 1px;
  transition:
    flex-basis 450ms cubic-bezier(0.22, 0.61, 0.36, 1),
    opacity 320ms ease 140ms,
    border-width 0ms;
}

@media (prefers-reduced-motion: reduce) {
  .analyst-col { transition: none; }
  .analyst-col.is-open { transition: none; }
}

.analyst-col > * { flex: 1; min-height: 0; }

/* Hero */
.hero {
  /* Pinned to the top of .main-col, which is the scroll container.  Needs an
     opaque backdrop or the content scrolling underneath shows through. */
  position: sticky;
  top: 0;
  z-index: 5;
  margin-right: -24px;
  padding: 24px 24px 10px 0;
  background: rgba(14, 18, 32, 0.88);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border);
}

/* Narrow column: the hero wraps to two rows here, so pinning it eats space
   the viewer needs for the content itself.  Same threshold the steps use, so
   the header un-pins exactly when the layout goes single-column. */
@container main (max-width: 660px) {
  .hero {
    position: static;
    margin-right: 0;
    padding: 24px 0 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: 0;
  }
}

.hero-top { display: flex; align-items: center; gap: 16px; }
.hero-top > div:nth-child(2) { flex: 1; min-width: 0; }
.logo { width: 52px; height: 52px; }
.hero h1 {
  margin: 0;
  font-size: 30px;
  letter-spacing: -0.02em;
  background: linear-gradient(90deg, #ffffff, #aab3ff);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.title-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.beta-badge {
  flex: none;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-2);
  background: rgba(99, 110, 250, 0.16);
  border: 1px solid rgba(99, 110, 250, 0.3);
  border-radius: 999px;
  padding: 3px 9px;
}
.tagline { color: var(--muted); font-size: 14.5px; margin-top: 6px; max-width: 560px; }
.hero-ctas { display: flex; gap: 10px; flex-wrap: wrap; justify-content: flex-end; align-self: flex-start; }

/* Buttons */
.btn {
  display: inline-block;
  border: none;
  border-radius: 10px;
  padding: 11px 18px;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: filter 0.15s ease, transform 0.15s ease;
}
.btn:hover { filter: brightness(1.12); transform: translateY(-1px); }
.btn-primary {
  background: linear-gradient(90deg, var(--accent), var(--accent-2));
  color: #fff;
}
.btn-ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}
.btn-sm { padding: 9px 14px; font-size: 12.5px; white-space: nowrap; }
.link { color: var(--accent-2); font-size: 13px; text-decoration: none; align-self: center; }
.link:hover { text-decoration: underline; }

/* Steps */
.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
@container main (max-width: 660px) {
  .steps { grid-template-columns: minmax(0, 1fr); }
}
.step {
  display: flex;
  gap: 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 14px;
}
.step-num {
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  background: rgba(99, 110, 250, 0.18);
  color: var(--accent-2);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
}
.step-title { font-weight: 600; font-size: 13.5px; margin-bottom: 4px; }
.step-body { color: var(--muted); font-size: 12.5px; line-height: 1.5; }

/* Panels */
.panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 16px;
  box-sizing: border-box;
}

/* Vertical distribution of the main column.  Everything keeps its natural
   height and never shrinks; "Your datasets" is the one section that absorbs
   whatever height is left over, so the drop zone is as large as the viewport
   allows.  Below its min-height the column scrolls as before. */
.main-col > * { flex: none; }

.data-panel {
  /* Grows into spare height, but never shrinks below its own content: with
     shrink enabled, a column too tall to fit squeezed this panel down to
     min-height and the dataset chips painted outside it, over the section
     below.  The column scrolls instead. */
  flex: 1 0 auto;
  min-height: 220px;
  display: flex;
  flex-direction: column;
}
/* Nothing left to grow into once the drop zone has collapsed, so the card
   shrinks to its content instead of leaving a tall empty bottom half. */
.data-panel:has(.upload.is-ready) { flex: 0 0 auto; min-height: 0; }
/* dcc.Upload wraps its children in an unclassed div, so the class we set
   lands one level in.  Pass the panel's spare height through the wrapper. */
#upload { display: flex; flex: 1 1 auto; min-height: 132px; }
#upload:has(.upload.is-ready) { flex: none; min-height: 0; }
/* Holds the drop zone and the samples line, below the confirmation and the
   chips.  Spaced off them only when there is something above it to clear. */
.add-data { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.status:not(:empty) ~ .add-data,
.chips:not(:empty) ~ .add-data { margin-top: 16px; }
.panel-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
  margin-bottom: 12px;
}
/* The upload target is the panel's primary action, so it takes the slack. */
.upload {
  flex: 1 1 auto;
  min-height: 132px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 20px;
  border: 1.5px dashed rgba(255, 255, 255, 0.28);
  border-radius: 12px;
  background: rgba(99, 110, 250, 0.04);
  box-sizing: border-box;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}
.upload:hover {
  border-color: var(--accent);
  background: rgba(99, 110, 250, 0.10);
}
.upload-title { font-size: 17px; font-weight: 650; color: var(--text); }
.upload-sub { font-size: 13px; color: var(--muted); margin-top: 6px; }

/* With data loaded the drop zone is no longer the thing to do, so it stops
   being a drop zone: no box, no fill, just a quiet link the size of the
   samples line.  Dropping a file on it still works.  Together with the copy
   swap in upload_copy() and the confirmation now sitting above it, that is
   what points a viewer with data at the chat panel instead. */
.upload.is-ready {
  flex: none;
  min-height: 0;
  width: fit-content;
  padding: 0;
  border: none;
  background: none;
  justify-content: flex-start;
  text-align: left;
}
.upload.is-ready:hover { border-color: transparent; background: none; }
.upload-add {
  font-size: 12.5px;
  color: var(--accent-2);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.upload.is-ready:hover .upload-add { color: var(--text); }
/* The samples were the alternative to a first upload; with data loaded there
   is nothing left for them to be an alternative to. */
.add-data:has(.upload.is-ready) .sample-row { display: none; }

/* Samples: deliberately quiet, so the upload reads as the thing to do. */
.sample-row {
  flex: none;
  margin-top: 12px;
  text-align: center;
  font-size: 12.5px;
  color: var(--muted);
}
.sample-lead { color: var(--muted); }
.sample-link {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--accent-2);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.sample-link:hover { color: var(--text); }
.status { color: var(--muted); font-size: 12.5px; }
.status:empty { display: none; }
/* The callouts space themselves off whatever precedes them; the first one in
   the card sits directly under the panel title, which has its own margin. */
.status > :first-child { margin-top: 0; }
.status:empty + .chips { margin-top: 0; }
/* A refused upload is the one thing in this panel a viewer must not miss, so
   it gets a callout in the page's only red, and slides in to catch the eye
   the way the muted status line never did. */
.status-error {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid rgba(255, 107, 107, 0.4);
  border-left: 3px solid var(--danger);
  border-radius: 10px;
  background: rgba(255, 107, 107, 0.12);
  color: #ffdada;
  font-size: 13.5px;
  animation: status-in 400ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
.status-error-icon {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 999px;
  background: var(--danger);
  color: #2a0f0f;
  font-size: 12px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* A load gets the mirror of that callout, in green: the viewer has just done
   the page's main action and needs to see it land, and be told that the chat
   panel is where they go next. */
.status-ok {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid rgba(0, 204, 150, 0.35);
  border-left: 3px solid var(--success);
  border-radius: 10px;
  background: rgba(0, 204, 150, 0.10);
  color: var(--text);
  font-size: 13.5px;
  animation: status-in 400ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
.status-ok-icon {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 999px;
  background: var(--success);
  color: #06281e;
  font-size: 11px;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
}
.status-ok-next { color: var(--muted); font-size: 12.5px; margin-top: 3px; }

@keyframes status-in {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .status-error, .status-ok { animation: none; }
}
.chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.chips:empty { display: none; }
/* Says what the chips are, so they answer "which datasets did I add?".  Full
   width so the chips always start on their own row, however many there are. */
.chips-label {
  flex: 0 0 100%;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--success);
}
.chip {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 5px 12px;
  font-size: 12px;
}

/* How to build your own */
.build-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  align-items: start;
}
@media (max-width: 900px) {
  .build-grid { grid-template-columns: 1fr; }
}
.build-grid { align-items: stretch; }
.build-steps { display: flex; flex-direction: column; gap: 14px; }
.build-step { display: flex; gap: 12px; }
/* Code card: filename + download on a header strip above the source. */
.build-code-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  background: var(--panel-2);
  /* Tracks the viewport instead of sitting at a fixed cap: fill the height
     available, less a buffer above and below, so the card stays a readable
     size on short screens and grows on tall ones.  Floor and ceiling keep it
     between ~7 lines and the file's natural height.  The source scrolls
     inside.  The dvh pass wins where supported; the vh line is the fallback
     for browsers without it. */
  max-height: clamp(190px, calc(100vh - 520px), 400px);
  max-height: clamp(190px, calc(100dvh - 520px), 400px);
  display: flex;
  flex-direction: column;
  min-height: 0;
}
.build-code-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 8px 8px 14px;
  border-bottom: 1px solid var(--border);
  background: rgba(255, 255, 255, 0.02);
}
.build-filename {
  font-size: 12px;
  color: var(--muted);
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
}
/* Python highlighting.  Classes come from highlight_python() in app.py, so
   the palette lives here with the rest of the theme rather than in an
   imported Pygments stylesheet. */
.tok-comment     { color: #6b7a99; }
.tok-docstring   { color: #7f8fb0; font-style: italic; }
.tok-keyword     { color: #a78bfa; }
.tok-string      { color: #7fd1b9; }
.tok-number      { color: #f0a878; }
.tok-function    { color: #7dd3fc; }
.tok-builtin     { color: #8ab4f8; }
.tok-decorator   { color: #f0a878; }
.tok-operator    { color: #9aa5bd; }
.tok-punctuation { color: #8b95ad; }

.build-code {
  margin: 0;
  padding: 12px 14px;
  flex: 1 1 auto;
  min-height: 0;
  overflow: auto;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--text);
  font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
}
/* Feedback CTA */
.feedback {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 14px;
  padding: 16px 18px;
}
.feedback-copy { min-width: 0; }
.feedback-title { font-weight: 600; font-size: 14px; margin-bottom: 4px; }
.feedback-body {
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
  max-width: 620px;
}

/* Footer */
.footer {
  margin-top: auto; /* hold the footer at the bottom of the column */
  background: linear-gradient(90deg, rgba(99, 110, 250, 0.16), rgba(25, 211, 243, 0.08));
  border: 1px solid rgba(99, 110, 250, 0.3);
  border-radius: 14px;
  padding: 16px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 13.5px;
}
.footer-links { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }

@media (max-width: 980px) {
  .page { flex-direction: column; height: auto; }
  .main-col { overflow: visible; }
  .hero {
    position: static;
    margin-right: 0;
    padding: 24px 0 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: 0;
  }
  /* Stacked, so the panel collapses vertically instead of horizontally:
     height drives it and flex-basis must not fight that.  min-height: 0 is
     the load-bearing part — a flex item's automatic minimum size otherwise
     floors a closed panel at its content height, leaving a dead band under
     the footer. */
  .analyst-col {
    min-width: 0;
    max-width: none;
    margin-left: 0;
    flex-basis: auto;
    height: 0;
    min-height: 0;
    margin-top: 0;
    transition:
      height 450ms cubic-bezier(0.22, 0.61, 0.36, 1),
      opacity 300ms ease 120ms,
      border-width 450ms step-end;
  }
  .analyst-col.is-open {
    /* flex-basis: 50% comes from the desktop rule and resolves against the
       page's auto height here, which collapses to content size — so the
       basis has to hand control back to `height`. */
    flex-basis: auto;
    height: 80vh;
    margin-top: 20px;
    /* Matches the page's 24px left padding, so the panel is centred rather
       than running off the right edge. */
    margin-right: 24px;
    border-radius: 14px;
    transition:
      height 450ms cubic-bezier(0.22, 0.61, 0.36, 1),
      opacity 320ms ease 140ms,
      border-width 0ms;
  }
}

/* Mobile hero: logo + title on one row, CTAs drop to a full-width row below */
@media (max-width: 760px) {
  .hero-top { flex-wrap: wrap; }
  .hero-top > div:nth-child(2) { flex: 1 1 calc(100% - 68px); }
  .hero h1 { font-size: 24px; }
  .hero-ctas { flex: 1 1 100%; justify-content: stretch; margin-top: 12px; }
  .hero-ctas .btn { flex: 1; text-align: center; }
}
