/*
 * Application-wide styles.
 */

/* Formula intellisense — the CM6 wrapper masquerades as a form-control.
   `:focus-within` lights up the wrapper when the embedded editor takes
   focus, so it gets the same blue ring a plain <input> would. The editor
   itself strips its outline (see `formulaTheme` in intellisense.js). */
.formula-cm-wrapper {
  cursor: text;
  background-color: var(--bs-body-bg, #fff);
}
.formula-cm-wrapper:focus-within {
  border-color: #86b7fe;
  box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
  outline: 0;
}
.formula-status {
  user-select: none;
}

/* Chat sidebar: viewport-bounded card with an internally-scrolling message
   panel and a sticky input footer. Without this the chat card grows with
   content, the page (not the panel) scrolls, and the input ends up below
   the visible viewport. */
.chat-card {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 90px);
}
.chat-card .chat-panel {
  flex: 1 1 auto;
  overflow-y: auto;
  min-height: 200px;
}
.chat-card .card-header,
.chat-card .card-footer {
  flex: 0 0 auto;
}
.chat-message + .chat-message {
  margin-top: 0.75rem;
}
.chat-message {
  border-left: 3px solid transparent;
  padding-left: 0.5rem;
}
.chat-message.user {
  border-left-color: #6c757d;
}
.chat-message.assistant {
  border-left-color: #0d6efd;
}

/* Widget drag-and-drop layout */
.widget-slot {
  cursor: grab;
  /* The resize handle inside is absolutely positioned; this is its
     containing block. */
  position: relative;
}
.widget-slot.dragging { opacity: 0.4; }

/* Drag handle on the slot's right edge for column-span resize. Subtle
   by default (so it doesn't visually fight the chart), full-opacity on
   hover. The whole rule set is hidden on print below via the same
   media query that already drops chat/nav/buttons. */
.widget-resize-handle {
  position: absolute;
  top: 0;
  right: -2px;        /* sit half-on, half-off the gutter */
  width: 6px;
  height: 100%;
  cursor: ew-resize;
  background-color: #0d6efd;
  opacity: 0.25;
  border-radius: 3px;
  z-index: 5;
  transition: opacity 0.15s;
}
.widget-resize-handle:hover { opacity: 1; }
/* While a resize drag is in flight, override the body cursor so the
   ew-resize cursor sticks even when the pointer drifts off the handle. */
body.widget-resizing,
body.widget-resizing * { cursor: ew-resize !important; user-select: none; }
.widget-row.drop-target,
.widget-row-new.drop-target {
  outline: 2px dashed #0d6efd;
  outline-offset: 4px;
  border-radius: 6px;
}
.widget-row-new {
  opacity: 0.55;
  transition: opacity 0.15s;
}
.widget-row-new:hover { opacity: 1; }
/* Inline new-row zones sit BETWEEN every existing row, so there can be
   many on the page. Tone them down so they don't overwhelm the layout —
   they still snap to full opacity on hover or when a drag is in progress
   (a sibling .dragging triggers the body-level affordance). */
.widget-row-new.inline {
  opacity: 0.2;
  font-size: 0.75rem;
}
.widget-row-new.inline:hover,
.widget-row-new.inline.drop-target { opacity: 1; }
.border-dashed { border-style: dashed !important; }

/* Turbo Frames are custom elements whose default display is `inline`.
   That means any widget rendered inside one — and especially Vega
   charts with `width: "container"` — measure their parent at width=0
   and render to nothing. Forcing block display gives the frame the
   surrounding col's width as it should have all along. */
turbo-frame { display: block; }

/* Markdown widgets — redcarpet-rendered narrative inside a card.
   Minimal: snug list/heading margins so the body reads as a single
   block. Inherits Bootstrap typography otherwise. */
.widget-markdown {
  padding: 0.25rem 0.5rem;
}
.widget-markdown > :first-child { margin-top: 0; }
.widget-markdown > :last-child  { margin-bottom: 0; }
.widget-markdown h1,
.widget-markdown h2,
.widget-markdown h3,
.widget-markdown h4 { margin-top: 1rem; margin-bottom: 0.5rem; }
.widget-markdown ul,
.widget-markdown ol { padding-left: 1.5rem; }
.widget-markdown pre {
  background: #f5f5f5;
  padding: 0.5rem 0.75rem;
  border-radius: 4px;
  overflow-x: auto;
}
.widget-markdown code {
  background: #f5f5f5;
  padding: 0.05rem 0.25rem;
  border-radius: 3px;
  font-size: 0.9em;
}
.widget-markdown pre code { background: transparent; padding: 0; }
.widget-markdown table {
  border-collapse: collapse;
  margin: 0.5rem 0;
}
.widget-markdown th,
.widget-markdown td {
  border: 1px solid #dee2e6;
  padding: 0.25rem 0.5rem;
}

/* Vega-embed default styling is `display: inline-block` with width
   set by `.vega-embed .chart-wrapper.fit-x` — a DESCENDANT selector
   that only matches a nested .chart-wrapper. Single-view charts
   (which is everything we author) don't have that nested wrapper, so
   the outer `.vega-embed.fit-x` element gets no width rule and
   collapses to inline-block's content width = 0. Override to block +
   full width so Vega can measure the container properly. */
.vega-embed { display: block; width: 100%; }
.vega-embed > svg { width: 100%; max-width: 100%; }

/* Hide the horizontal scrollbar on the dashboard tab strip — the strip
   still scrolls via touch/wheel; the visible bar was ugly when present.
   Add a soft fade on the right edge so overflow is discoverable. */
#dashboard-page-tabs::-webkit-scrollbar { display: none; }
#dashboard-page-tabs { scrollbar-width: none; -ms-overflow-style: none; }

/* Widget-rendered tables (output of DeclarativeRenderer.render_table)
   should never wrap. A wrapped cell shoves the row down and breaks
   alignment with sibling tables on the same page. Long text overflows
   horizontally; the parent `.table-responsive` wrapper already
   provides horizontal scroll so wide tables stay reachable. Long-text
   columns that NEED wrapping (e.g. review comment bodies) can opt back
   in by setting `white-space: normal` on the specific <td>. */
.table-responsive .table td,
.table-responsive .table th { white-space: nowrap; }

/* Dashboard pages: rendered as Bootstrap tabs on screen; expanded into
   page-breaking sections on print. */

@page {
  /* Landscape suits the 12-col widget grid; portrait squeezes wide
     tables. 1cm margin balances ink-on-page against printer-safe
     gutters across the common printers. Bottom margin slightly
     wider (1.4cm) to reserve room for the @bottom-* footer boxes
     (page numbers + vibeinsights.io attribution). */
  size: A4 landscape;
  margin: 1cm 1.2cm 1.4cm 1.2cm;

  @bottom-left {
    content: "Generated by vibeinsights.io";
    font-family: system-ui, sans-serif;
    font-size: 8pt;
    color: #888;
  }
  @bottom-right {
    content: "Sida " counter(page) " av " counter(pages);
    font-family: system-ui, sans-serif;
    font-size: 8pt;
    color: #888;
  }
}

/* Named pages for per-DashboardPage orientation overrides. Pages that
   set `dashboard_page.print_orientation` get the matching class on
   their .dashboard-page section, and CSS named-page support pulls the
   right `@page` rule. Modern browsers (Chromium / Firefox) honor this;
   safe degradation = the dashboard-wide default above. Footer boxes
   redeclared per named-page rule — @page properties don't cascade
   through named-page inheritance. */
@page portrait_page {
  size: A4 portrait;
  margin: 1cm 1.2cm 1.4cm 1.2cm;
  @bottom-left {
    content: "Generated by vibeinsights.io";
    font-family: system-ui, sans-serif;
    font-size: 8pt;
    color: #888;
  }
  @bottom-right {
    content: "Sida " counter(page) " av " counter(pages);
    font-family: system-ui, sans-serif;
    font-size: 8pt;
    color: #888;
  }
}
@page landscape_page {
  size: A4 landscape;
  margin: 1cm 1.2cm 1.4cm 1.2cm;
  @bottom-left {
    content: "Generated by vibeinsights.io";
    font-family: system-ui, sans-serif;
    font-size: 8pt;
    color: #888;
  }
  @bottom-right {
    content: "Sida " counter(page) " av " counter(pages);
    font-family: system-ui, sans-serif;
    font-size: 8pt;
    color: #888;
  }
}
.dashboard-page.print-portrait  { page: portrait_page; }
.dashboard-page.print-landscape { page: landscape_page; }

/* ── Table templates ───────────────────────────────────────────────
   Three named visual templates a widget's TablePresentation#template
   picks from. `normal` is the default Bootstrap-stripe look (no rule
   block — the base .table.table-striped already covers it).

   Per-widget row_color_indexes / column_color_indexes / Metric#color
   paint INLINE styles in the renderer, which beat these CSS rules —
   author colors still win on top of the template.

   NOTE: the accent hexes here (e.g. #c3d598, #D8E4BC for tsb_green)
   ALSO live in `TEMPLATE_PALETTES` in
   `app/pipelines/model/declarative_widget_spec.rb`. This CSS handles
   the static chrome (header bg, banner accent); the Ruby palette
   handles per-cell tints. Same hex family, different roles. If you
   retune a template's color story, touch BOTH places. */

/* business: P&L-report aesthetic. White everywhere, no fills, no
   stripes — just horizontal rules and typography. Header is bold
   text under a top + bottom rule. Section banners read as bold
   typography, not coloured bars. Use for board-deck pages and
   month-end reports where stripes / fills read as decoration noise.
   NOTE: no `!important` on the stripe rule — inline styles painted by
   the renderer's column_color_indexes resolution must win over the
   template stripe. Specificity alone is enough to beat Bootstrap's
   .table-striped default. */
.table-template-business table {
  background: transparent;
}
.table-template-business table.table-striped > tbody > tr:nth-of-type(odd) > * {
  background: transparent;
}
.table-template-business table thead th {
  background: transparent;
  color: #212529;
  border-top: 1px solid #212529;
  border-bottom: 1px solid #212529;
  font-weight: 600;
}
/* Section banner row: bold black text on white, single top rule —
   the typographic equivalent of an accounting page's section header. */
.table-template-business table.table-secondary,
.table-template-business table tr.table-secondary,
.table-template-business table tr.table-secondary > * {
  background: transparent !important;
  color: #212529 !important;
  border-top: 1px solid #adb5bd !important;
  font-weight: 600;
}
/* Totals footer: single rule on top, bold — accounting "underline the
   total" convention. */
.table-template-business table tfoot tr {
  border-top: 1px solid #212529;
}
.table-template-business table tfoot td {
  background: transparent !important;
  font-weight: 700;
}

/* tsb_green: sage / olive green branded look. Palette lifted from
   widget 14.1's column_colors (#D8E4BC, #c3d598) — subtle Excel-era
   corporate greens, not Pantone forest. Header carries the deeper
   shade; section banners and column tint use the lighter one. */
/* No `!important` on the stripe — inline column_color_indexes
   resolution must win on cells that carry a column tint. Higher
   specificity than Bootstrap's .table-striped alone is enough. */
.table-template-tsb_green table.table-striped > tbody > tr:nth-of-type(odd) > * {
  background: #f5f8ee;
}
.table-template-tsb_green table thead th {
  background: #c3d598;
  color: #2a3d10;
  border-bottom: 2px solid #9bb86c;
}
.table-template-tsb_green table.table-secondary,
.table-template-tsb_green table tr.table-secondary,
.table-template-tsb_green table tr.table-secondary > * {
  background: #D8E4BC !important;
  color: #2a3d10 !important;
}

/* Copy-table-to-clipboard button: float top-right of the table widget's
   card-body. Visible in both edit and view modes so Erik can grab the
   rendered table for pasting into chat. */
.widget-copy-table-btn {
  position: absolute;
  top: 4px;
  right: 8px;
  z-index: 2;
  font-size: 0.85rem;
  line-height: 1;
  opacity: 0.5;
}
.widget-copy-table-btn:hover {
  opacity: 1;
}

@media print {
  .widget-copy-table-btn { display: none !important; }

  /* Flat background, no shadows — printers / PDFs render shadows as
     grey halos and waste ink on card backgrounds. `body.bg-light` is
     applied in the application layout for the on-screen dashboard
     look; its own `!important` rule has class-level specificity and
     beats a plain `body` selector, so the print override has to match
     the same specificity to win the cascade. */
  body, body.bg-light { background: #fff !important; }

  /* (Removed) earlier `.print-logo { position: fixed }` rule. Logo
     now flows inline in the page-heading row inside `.dashboard-
     page-content` (see print.html.erb) — Erik's preference: no
     position:fixed / no @page margin reservation / no body
     padding-top. Logo appears on the first sheet of each .dashboard-
     page; continuation sheets (when a table flows) don't repeat. */
  .card, .widget-card {
    box-shadow: none !important;
    border-color: #dee2e6 !important;
    background: #fff !important;
  }

  /* When the author opted out of the widget title (show_title=false),
     strip the card chrome entirely in print — no border, no padding,
     no background. The chart renders naked under the page heading,
     which is already naming the section. */
  .widget-card.widget-no-title {
    border: none !important;
    background: transparent !important;
  }
  .widget-card.widget-no-title .card-body {
    padding: 0 !important;
  }

  /* Bootstrap `.row` uses negative side margins to create gutters,
     and `.col-*` uses positive padding to cancel them. On screen
     that's fine — the container's own padding accommodates. In print
     the body is pinned at the @page-inner margin, so .row's negative
     margin pushes children outside the printable area and they get
     clipped on the right. Strip both for print so widget rows sit
     exactly at the body edges. */
  .row { margin-left: 0 !important; margin-right: 0 !important; }
  .col, [class^="col-"], [class*=" col-"] {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }
  /* Bootstrap's `.col-md-N` widths only apply at md (≥768px). Headless
     Chrome's print viewport sits at the page-area width (~1032px in
     landscape) but in some setups print-media evaluates layout BEFORE
     the print page dimensions are known. Force the widget-grid widths
     explicitly so a col-md-8 + col-md-4 row stays side-by-side instead
     of stacking vertically — the latter wastes a printed sheet per
     row. Same for the col-md-6 + col-md-6 pattern (two equal charts). */
  .col-md-8  { flex: 0 0 66.66666667% !important; max-width: 66.66666667% !important; }
  .col-md-6  { flex: 0 0 50%           !important; max-width: 50%           !important; }
  .col-md-4  { flex: 0 0 33.33333333% !important; max-width: 33.33333333% !important; }
  .col-md-12 { flex: 0 0 100%          !important; max-width: 100%          !important; }

  /* Force every tab pane visible regardless of .active / .show. */
  .tab-pane.dashboard-page {
    display: block !important;
    opacity: 1 !important;
    page-break-after: always;
    break-after: page;
    /* Fixed printable area per orientation. The inner
       .dashboard-page-content is what JS scales down to fit when the
       natural content height exceeds these bounds — see the
       beforeprint hook in print.html.erb. Width/height match the @page
       size minus the @page margins (1cm × 1.2cm = 297mm × 210mm −
       2cm − 2.4cm landscape; − 2.4cm − 2cm portrait). */
    width: calc(297mm - 2.4cm);
    height: calc(210mm - 2cm);
    overflow: hidden;
  }
  .tab-pane.dashboard-page.print-portrait {
    width: calc(210mm - 2.4cm);
    height: calc(297mm - 2cm);
  }
  /* When the page contains a `.widget-slot.widget-table`, the fit-JS
     skips fit-scaling so the table can flow over multiple sheets with
     <thead> repeating. The fixed height + overflow:hidden above would
     CLIP that flow on the very first sheet — which is the dashboard-
     view vs dashboard-print divergence Erik saw on Produkter (widget
     74): Aktiviteter rows missing from the PDF because they sat below
     the 190mm cutoff. Release both so the natural flow can do its
     work. */
  .tab-pane.dashboard-page:has(.widget-slot.widget-table) {
    height: auto;
    overflow: visible;
  }
  .tab-pane.dashboard-page:last-child {
    page-break-after: auto;
    break-after: auto;
  }

  /* Page heading: bigger + hairline rule below, more like a printed
     report's section header. Default Bootstrap h4 sits at ~17px regular
     weight — in print it reads as a small subheading instead of a
     section title. Bump to 16pt bold; bottom rule + padding live on
     the .dashboard-page-heading flex row (not the h4) so the rule
     spans the full content width including the logo's right edge. */
  .dashboard-page-content > .dashboard-page-heading {
    border-bottom: 1px solid #000 !important;
    padding-bottom: 4px !important;
    margin-bottom: 16px !important;
  }
  .dashboard-page-content .dashboard-page-title {
    font-size: 16pt !important;
    font-weight: 600 !important;
    line-height: 1.1 !important;
    margin-bottom: 0 !important;
  }

  /* Inner content gets transform-origin so JS scaling pivots from the
     top-left (the natural reading anchor). Scale factor itself is set
     inline by the beforeprint hook. */
  .dashboard-page-content {
    transform-origin: top left;
    width: 100%;
  }

  /* `.table-responsive` adds its own horizontal scroll wrapper so a
     wide table on a narrow page scrolls inside the wrapper. That hides
     the overflow from the auto-fit measurement (the wrapper's own
     scrollWidth stays at its container width). For print we want the
     table's natural width to bubble UP so the fit can zoom the whole
     dashboard-page-content to fit, leaving no clipped scroll-only
     content. Disable the inner scrolling for print only — the parent
     dashboard-page still has overflow: hidden as a safety net. */
  .table-responsive {
    overflow-x: visible !important;
  }

  /* Compact table typography at print: 35-row Resultat fits one A4
     portrait sheet instead of overflowing to a continuation. Body
     cells go to 8pt + 1px 4px padding (the Qlik-PDF density the
     dashboard is benchmarked against). Header stays at 9pt bold so
     the column titles still read at a glance. Applies to every
     `.widget-card table` in print regardless of template — the
     Resultat / Rullande 12 widgets all use the same row count
     threshold for spillover. */
  .widget-card table { font-size: 8pt; }
  .widget-card table thead th { font-size: 9pt; padding: 2px 4px !important; }
  .widget-card table tbody td,
  .widget-card table tfoot td { padding: 1px 4px !important; }

  /* Truncate long names in the first column so the table's natural
     width stays bounded — otherwise wide-table portrait pages (e.g.
     Kontrollerbara kostnader with 45-char Swedish account names)
     force the auto-fit to scale everything down too aggressively,
     shrinking the numeric columns we actually want to read. 50mm
     gives ~22 chars before ellipsis at the print font size; numbers
     in subsequent columns then render at their natural size. */
  .widget-card table td:first-child,
  .widget-card table th:first-child {
    max-width: 50mm;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Don't split a single widget across two pages — a half-chart on
     each side of a page break is unreadable. Bootstrap row needs to
     stay flexible (the cell still has to fit), so the avoid lives on
     the slot / card. Widget-rows also avoid break-inside when none
     of the row's widgets is a long-table opt-out: the row's natural
     height fits one sheet, and splitting (84 on sheet N, 85 on sheet
     N+1) wastes a sheet for the trailing widget. Rows that contain a
     `.widget-table` slot DO allow break-inside so the long table can
     flow naturally — the sibling chart still pages together with it. */
  /* widget-row deliberately NOT in this set — adding break-inside:
     avoid on the row pushed the entire row to the next page when
     it + the page heading didn't fit on one sheet, orphaning the
     heading. The slot/card protection is enough to keep a single
     widget intact; flex-wrap handles multi-row layouts naturally. */
  .widget-slot,
  .widget-card { break-inside: avoid; page-break-inside: avoid; }

  /* (Removed) earlier rule forced every widget in a row containing
     a `.widget-slot.widget-table` to 100% width to avoid the "ghost
     column" next to a tall table flowing across sheets. In practice
     tables are often short (Google reviews comments = 4 rows): the
     forced split wasted a sheet for the sibling widget. The natural
     Bootstrap flex layout fits both at their authored col-widths.
     If a future case has a genuinely tall table, address it by
     splitting onto its own dashboard page rather than re-introducing
     this catch-all rule. */

  /* Table widgets are the exception: long tables MUST split across
     pages, with <thead> repeating on each page (browser does that
     automatically for proper <thead>). The slot gets `.widget-table`
     from the dashboard view when the widget's presentation kind is
     "table" — opt back into normal splitting all the way down. */
  .widget-slot.widget-table,
  .widget-slot.widget-table .widget-card,
  .widget-slot.widget-table .table-responsive,
  .widget-slot.widget-table table {
    break-inside: auto;
    page-break-inside: auto;
  }
  /* Don't split an individual row across a page break — splits land
     between rows only. */
  .widget-slot.widget-table tr {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  /* Belt-and-suspenders: ensure <thead> is recognised as a header
     group so the browser repeats it on every page the table spans. */
  .widget-slot.widget-table thead {
    display: table-header-group;
  }
  /* Don't orphan the widget title at the bottom of the previous
     page when the table that follows it splits. */
  .widget-slot.widget-table .card-header {
    break-after: avoid;
    page-break-after: avoid;
  }

  /* ── Compact print sizing ────────────────────────────────────────
     Trim widget chrome so the natural-layout height is closer to the
     printable area — the auto-fit zoom on .dashboard-page-content
     still kicks in for genuine overflow, but pages that fit at a
     more generous scale now print bigger instead of being squeezed
     to ~66%. */

  /* Charts default to 280px min-height in the renderer. Print sees a
     narrower printable area than the screen viewport, so the chart
     gets less vertical real estate anyway — pull the min down so the
     declarative height stops dominating page layout. Charts that
     declared a larger explicit height still get it from `style=`. */
  [data-vega-payload] {
    min-height: 180px !important;
  }

  /* Card chrome shrinks aggressively for print. Header / body / row
     margins all set for screen reading; for print, every mm of
     vertical space we save is one more widget that fits at natural
     scale instead of being shrunk by the zoom hook. */
  .widget-card .card-header { padding: 0.15rem 0.4rem !important; }
  .widget-card .card-body   { padding: 0.25rem 0.4rem !important; }
  .widget-row { margin-bottom: 0.25rem !important; }
  .dashboard-page h4        { font-size: 1rem !important; margin: 0.25rem 0 !important; }
  .dashboard-page .small    { font-size: 0.75rem; }

  /* Hide editing / interactive chrome. The dashboard filter form is
     hidden too; the printable summary above (.d-print-block on
     .dashboard-filter-summary) carries the applied filter values
     instead so the printed page is self-explanatory. */
  .nav-tabs,
  .dashboard-page-actions,
  .widget-row-new,
  .widget-resize-handle,
  .chat-card,
  .dashboard-filter-form,
  nav,
  .btn { display: none !important; }
}

