/* Integra — creator report (report.html). The page an operator sends a client:
   one column, 860px, hairlines and whitespace, every figure exact and mono.
   Built to be read on screen in either theme and printed on white paper — the
   @media print block at the end is part of the page, not an afterthought.

   --row-hover, --rule and the rest live in tokens.css so this page follows the
   theme. Nothing here sets a raw colour except the print block, which defines
   its own surface (see the comment there). */

::selection { background: var(--blurple); color: var(--accent-ink); }

/* Subtle page-load fade — opacity only, gated on motion preference. */
@media (prefers-reduced-motion: no-preference) {
  body { animation: report-page-in 150ms var(--ease); }
  @keyframes report-page-in { from { opacity: 0; } }
}

/* No spinners: the data containers hold their height and fade in when the
   render lands. html.js-loading is set by a 2-line inline script in <head> and
   removed by js/report.js on every path out of the read — the render, the
   plain note, and the fatal catch — so a failure can never leave the page
   veiled. base.css zeroes the transition under prefers-reduced-motion. */
#report-tiles,
#report-growth,
#report-streak,
#report-missed,
#report-accounts,
#report-posts {
  transition: opacity 120ms var(--ease);
}

html.js-loading #report-tiles,
html.js-loading #report-growth,
html.js-loading #report-streak,
html.js-loading #report-missed,
html.js-loading #report-accounts,
html.js-loading #report-posts {
  opacity: 0;
}

.report-page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* One column for the whole document: header, report, footer. The lockup sits
   on the same left edge as the creator's name and the figures under it, so the
   printed page reads as one letterhead rather than three widths. */
.header-inner,
.report-main,
.footer-inner {
  width: 100%;
  max-width: 860px;
  margin: 0 auto;
  padding-left: var(--s5);
  padding-right: var(--s5);
}

/* ---- Header ---- */

.report-header {
  border-bottom: 1px solid var(--rule);
  background: var(--paper);
}

.report-header .header-inner {
  padding-top: var(--s4);
  padding-bottom: var(--s4);
  min-height: calc(41px + var(--s4) * 2); /* a .btn-sm's own height, so the bar never resizes */
  display: flex;
  align-items: center;
  gap: var(--s2) var(--s3);
  flex-wrap: wrap;
}

.lockup-link { display: inline-flex; align-items: center; }
/* One height for both themes: light and dark ship the same 1080×267 badged
   lockup, so the identity never moves or changes size across a theme flip. */
.lockup { height: 28px; width: auto; display: block; }

.header-divider {
  width: 1px;
  height: 18px;
  background: var(--rule);
  flex: none;
}

/* Right-edge group: range, theme, print. None of it prints. */
.header-controls {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--s2) var(--s3);
  flex-wrap: wrap;
}

/* Same toggle pattern as the dashboard's range control (app.css .toggle-btn),
   restated here because app.css is not loaded on this page. */
.toggle-group { display: flex; gap: var(--s2); }

.toggle-btn {
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  position: relative;
  min-height: 40px;
  min-width: 40px;
  display: inline-flex;
  align-items: flex-end;
  justify-content: center;
  padding: 12px 6px 4px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--graphite);
  transition: color var(--t-fast) var(--ease);
}

/* The active underline slides in over 150ms instead of snapping. The
   transparent 2px border above reserves its slot; ::after overlays it. */
.toggle-btn::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -2px;
  height: 2px;
  background: var(--blurple);
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--t-fast) var(--ease), opacity var(--t-fast) var(--ease);
}

.toggle-btn:hover { color: var(--ink); }
.toggle-btn[aria-pressed="true"] { color: var(--ink); }
.toggle-btn[aria-pressed="true"]::after { opacity: 1; transform: scaleX(1); }

/* Compact button size — same values as the app shell's .btn-sm. */
.btn-sm { padding: 13px 16px; font-size: 13px; }

/* ---- Main column ---- */

.report-main {
  flex: 1;
  padding-top: var(--s6);
  padding-bottom: var(--s8);
}

@media (min-width: 600px) {
  .report-main { padding-top: var(--s7); }
}

/* Page-level notes — no access, nothing on record, a read that failed.
   js/report.js fills this and hides #report-body, so the note replaces the
   report instead of heading a half-empty one. */
.report-note {
  color: var(--graphite);
  font-size: 15px;
  max-width: var(--measure);
  margin: 0;
}

.report-note + .report-note { margin-top: var(--s4); }

/* The one figure a note carries — a database message, a creator id — stays
   mono and stays quiet. */
.report-note.data { font-size: 13px; }

.note-action { margin-top: var(--s4); font-size: 15px; }

/* ---- Title block ---- */

.report-title { padding-bottom: var(--s6); }

.report-name {
  font-weight: 400;
  font-size: clamp(28px, 4.4vw, 40px);
  letter-spacing: -0.01em;
  line-height: 1.1;
  margin: 0 0 var(--s2);
}

.report-handle {
  margin: 0 0 var(--s3);
  font-size: 15px;
  color: var(--graphite);
}

/* The exact window, then the days covered — one mono line on desktop, two
   when the column is narrow. The separator is drawn by CSS so the text stays
   copyable as two plain facts. */
.report-window {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1) var(--s3);
  margin: 0;
  font-size: 13px;
  color: var(--graphite);
}

/* :not([hidden]) — a window with no dates on record must not leave the
   separator leading the line that follows it. */
.report-window > span:not([hidden]) + span::before {
  content: '· ';
  color: var(--rule);
}

/* ---- Sections ---- */

.report-section {
  border-top: 1px solid var(--rule);
  padding: var(--s6) 0;
}

.section-label { display: block; margin: 0 0 var(--s4); }

.sub-label { display: block; margin-bottom: var(--s3); }

/* ---- Headline figures ---- */
/* Hairline-separated columns, no boxes — the dashboard's stat tiles, in the
   report's own column. */

.figures {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  min-height: 92px; /* label + 24px figure + sub line, reserved pre-render */
}

.figure {
  min-width: 0;
  padding: var(--s2) var(--s3) var(--s2) 0;
}

.figure + .figure {
  border-left: 1px solid var(--rule);
  padding-left: var(--s3);
}

.figure-value {
  font-weight: 500;
  font-size: 24px;
  line-height: 1.2;
  margin-top: var(--s2);
  overflow-wrap: anywhere;
}

.figure-sub {
  font-size: 13px;
  color: var(--graphite);
  margin-top: var(--s1);
}

/* ---- Growth ---- */

#report-growth { min-height: 280px; }

/* The chart's own empty state and the page's other plain lines share this
   register: a fact in mono, graphite, no box around it. */
.plain-note {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--graphite);
  margin: 0;
  padding: var(--s4) 0;
  max-width: var(--measure);
}

/* ---- Attendance ---- */
/* Two columns of equal weight: the streak and the days without a post. The
   bad number is set in the same size as the good one, beside it, not under a
   fold — the brief's rule, expressed in the grid. */

/* Stretched, not start-aligned: the hairline between the two halves is the
   divider for the whole section, and a half-height rule beside a full-height
   calendar reads as an unfinished line. */
.attendance {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s6);
  align-items: stretch;
}

.attendance-streak,
.attendance-missed { min-width: 0; }

.attendance-missed {
  border-left: 1px solid var(--rule);
  padding-left: var(--s6);
}

#report-streak { overflow-x: auto; min-height: 160px; }

#report-missed { min-height: 120px; }

/* Same figure register as the streak module prints under its calendar
   (28px mono + 15px unit), so neither number outranks the other. Shared by
   the missed-day count and by the streak's own fallback figure. */
.big-figure { line-height: 1; margin-bottom: var(--s3); }

.big-num {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 28px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.big-unit {
  font-size: 15px;
  color: var(--graphite);
  margin-left: 6px;
}

/* The dates themselves — mono, wrapping into as many columns as fit. A long
   list is the point: it is not folded away or capped. */
/* Fixed 80px tracks, not 1fr: a fortnight of missed days should read as a
   list, and stretched columns push two dates to opposite ends of the page. */
.missed-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, 80px);
  gap: var(--s1) var(--s3);
}

.missed-list li {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
}

/* No missed days is good news, and it is said as plainly as the bad kind:
   body size, ink, no decoration. */
.missed-none {
  margin: 0;
  font-size: 15px;
  color: var(--ink);
  max-width: var(--measure);
}

/* ---- Per platform ---- */
/* A hairline table, not a card. The wrapper scrolls on narrow screens so the
   page itself never does. */

.table-scroll {
  overflow-x: auto;
  /* The focus ring on a scrolled cell link needs room to draw. */
  padding-bottom: 2px;
}

.platform-table {
  width: 100%;
  min-width: 620px;
  border-collapse: collapse;
}

.platform-table th,
.platform-table td {
  padding: var(--s3) var(--s4) var(--s3) 0;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}

.platform-table th { vertical-align: bottom; }

.platform-table tbody tr:last-child td { border-bottom: 0; }

.platform-table .col-num,
.platform-table td.col-num {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 13px;
  padding-right: 0;
}

.platform-table th.col-num { padding-right: 0; }

/* Platform cell: 16px glyph + name, the same register as the dashboard rows. */
.cell-platform {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: 15px;
}

.cell-glyph {
  display: inline-flex;
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--ink);
}
.cell-glyph svg { width: 16px; height: 16px; display: block; }

.cell-handle {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--ink);
}

/* The handle opens that account's own page. It takes the link colour back off
   .cell-handle above, which would otherwise hold it at Ink; the hover
   underline is base.css's, so the cell matches every other link on the site. */
a.cell-handle { color: var(--blurple-text); }
a.cell-handle:hover { color: var(--blurple-text-hover); }

/* The follower delta reads as a fact, not a verdict: no green, no red. */
.platform-table td.col-delta { color: var(--graphite); }

.platform-foot {
  margin: var(--s3) 0 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--graphite);
}

/* ---- Top posts ---- */
/* js/feed.js owns the row structure and injects its own stylesheet. These are
   the report's additions: the hover wash and the thumbnail column, matching
   the dashboard (assets/css/app.css), which is not loaded here. */

#report-posts .integra-feed-row {
  position: relative;
  z-index: 0;
  padding: var(--s3) 0;
}

#report-posts .integra-feed-row::before {
  content: '';
  position: absolute;
  inset: 0 calc(-1 * var(--s3));
  z-index: -1;
  border-radius: var(--radius);
  pointer-events: none;
  transition: background-color var(--t-fast) var(--ease);
}

#report-posts .integra-feed-row:hover::before { background: var(--row-hover); }

#report-posts .integra-feed-row--thumb {
  grid-template-columns: 16px minmax(0, 1fr) auto 56px;
}

#report-posts .integra-feed-thumb {
  grid-column: 4;
  grid-row: 1;
  display: block;
  line-height: 0;
}

#report-posts .integra-feed-thumb img {
  display: block;
  width: 56px;
  height: 74px;
  object-fit: cover;
  background: var(--row-hover);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  transition: border-color var(--t-fast) var(--ease);
}

/* No hairline until the picture is really there (js/feed.js sets data-loaded
   on the load event) — a framed empty box reads as a broken image. */
#report-posts .integra-feed-thumb img:not([data-loaded]) { border-color: transparent; }

#report-posts .integra-feed-view:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ---- Footer ---- */

.report-footer { border-top: 1px solid var(--rule); }

.report-footer .footer-inner {
  padding-top: var(--s5);
  padding-bottom: var(--s5);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3) var(--s4);
  flex-wrap: wrap;
}

.report-footer .tagline { margin: 0; }

.footer-record {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s3);
}

.footer-record img { display: block; width: 16px; height: 16px; }

/* ---- Responsive ----------------------------------------------------------
   Every query below is scoped to `screen`. A printed page is about 673 CSS px
   wide (A4 less 2 × 16mm), which would otherwise trip the 760px fold and print
   the figures 2-up and the attendance stacked — paper has the room for the
   full layout, and the print block below is what decides how it looks. */

@media screen and (max-width: 760px) {
  .figures { grid-template-columns: 1fr 1fr; }

  .figure:nth-child(odd) { border-left: 0; padding-left: 0; }
  .figure:nth-child(-n+2) { padding-top: 0; }
  .figure:nth-child(n+3) { border-top: 1px solid var(--rule); padding-top: var(--s3); }

  .attendance { grid-template-columns: 1fr; gap: var(--s5); }

  .attendance-missed {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--rule);
    padding-top: var(--s5);
  }

  /* Stacked, the reserved height sits under a one-line sentence instead of
     beside a calendar, so it reads as a hole in the page. Nothing moves when
     the render lands either way: the section below it is already the last
     thing on that screenful. */
  #report-missed { min-height: 0; }
}

@media screen and (max-width: 640px) {
  /* Feed thumb shrinks to 48×64, as it does on the dashboard. */
  #report-posts .integra-feed-row--thumb {
    grid-template-columns: 16px minmax(0, 1fr) auto 48px;
  }

  #report-posts .integra-feed-thumb img { width: 48px; height: 64px; }

  /* The seven-column table cannot fit a phone: 620px of columns in a 358px
     column means the reader drags the table sideways to find the views, and
     the figures leave the screen one at a time. Folded, each account is one
     hairline record — platform, handle, then every figure against its own
     name. Same hairlines, same mono figures, no boxes; the header row is the
     only thing dropped, because each cell now carries its own label. */
  .table-scroll { overflow: visible; }

  .platform-table { min-width: 0; }

  .platform-table thead { display: none; }

  .platform-table,
  .platform-table tbody,
  .platform-table tr,
  .platform-table td { display: block; }

  .platform-table tr {
    padding: var(--s3) 0;
    border-bottom: 1px solid var(--rule);
  }

  .platform-table tbody tr:last-child { border-bottom: 0; }

  .platform-table td {
    padding: 0;
    border: 0;
    white-space: normal;
  }

  .platform-table td + td { margin-top: var(--s1); }

  .platform-table td[data-label] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s3);
  }

  .platform-table td[data-label]::before {
    content: attr(data-label);
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 12px;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--graphite);
  }

  /* The handle sits under the platform name as the record's second line. */
  .platform-table td:nth-child(2) { margin-top: 2px; margin-bottom: var(--s2); }
}

@media screen and (max-width: 420px) {
  /* 20px mono keeps a 10-digit figure on one line in the ~124px 2-up column;
     overflow-wrap:anywhere on .figure-value remains the last-resort guard. */
  .figure-value { font-size: 20px; }

  .header-inner,
  .report-main,
  .footer-inner {
    padding-left: var(--s4);
    padding-right: var(--s4);
  }
}

/* ---- Print ---------------------------------------------------------------
   The report leaves the building as paper or a PDF. On paper the theme is not
   the reader's choice, so the tokens are redefined for one surface: white
   sheet, black text, hairlines dark enough to survive a laser printer. These
   are the only literal colours in the file, and they sit in the same place
   tokens.css puts them — every rule above still reads var(--ink), so nothing
   below this line had to be restyled.

   --blurple keeps its value: the chart line, the streak cells and the active
   marks are the brand accent on paper too, and they degrade to grey on a
   monochrome printer without losing their shape. */
@media print {
  /* Four selectors, not one: a media query adds no specificity, so a bare
     `:root` here (0,1,0) loses to tokens.css's `:root[data-theme="dark"]` and
     `:root:not([data-theme="light"])` (both 0,2,0) and the sheet prints the
     dark theme's near-white text onto white paper. Matching their specificity
     from a sheet that loads later is what wins, in every theme state. */
  :root,
  :root[data-theme="dark"],
  :root[data-theme="light"],
  :root:not([data-theme="light"]) {
    color-scheme: light;

    --ink: #000000;
    --paper: #FFFFFF;
    --graphite: #3F3F46;   /* metadata, dark enough to print cleanly */
    --rule: #A9A8B2;       /* hairlines survive toner and screening */
    --row-hover: #EFEFF2;  /* empty streak cells keep a body */
    --blurple-text: #2F3BB3;
    --blurple-text-hover: #2F3BB3;
    --band-surface: #FFFFFF;
    --band-ink: #000000;
    --btn-fill: #000000;
    --btn-ink: #FFFFFF;

    /* Ink marks, whatever theme the screen was showing. */
    --show-on-light: block;
    --show-on-dark: none;
  }

  @page {
    margin: 16mm;
  }

  html, body {
    background: var(--paper);
    color: var(--ink);
  }

  /* The veil never applies to paper, whatever state the boot ended in. */
  html.js-loading #report-tiles,
  html.js-loading #report-growth,
  html.js-loading #report-streak,
  html.js-loading #report-missed,
  html.js-loading #report-accounts,
  html.js-loading #report-posts {
    opacity: 1;
  }

  /* Controls do not print: the range toggle, the theme toggle, the Print
     button. The lockup stays — it is the letterhead. */
  .header-controls { display: none; }

  .report-header,
  .report-footer { background: transparent; }

  .report-header .header-inner { min-height: 0; padding-top: 0; }

  /* The page's own margins come from @page; the column stops fighting them. */
  .header-inner,
  .report-main,
  .footer-inner {
    max-width: none;
    padding-left: 0;
    padding-right: 0;
  }

  .report-main { padding-top: var(--s5); padding-bottom: var(--s5); }

  /* A section is a unit: it does not split across a sheet, and a heading never
     ends one. */
  .report-section,
  .report-title,
  .attendance-streak,
  .attendance-missed,
  .figures,
  .platform-table tr {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  h1, h2, .section-label, .sub-label {
    break-after: avoid;
    page-break-after: avoid;
  }

  /* The chart and the streak grid are the two blocks made of fill rather than
     text. Without this the browser drops their backgrounds and prints an empty
     frame where the record should be. */
  #report-growth,
  #report-growth svg,
  #report-streak,
  #report-streak .igs-cell,
  #report-posts .integra-feed-thumb img {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Both are scroll containers on screen; on paper there is nothing to scroll,
     so they print whole. */
  #report-streak,
  .table-scroll { overflow: visible; }

  .platform-table { min-width: 0; }

  /* Rows keep their hairlines; the screen-only hover wash does not print. */
  #report-posts .integra-feed-row::before { display: none; }

  /* Link colour is fine on paper; the underline is not — it reads as an
     error mark on a printed figure. */
  a { text-decoration: none; }

  /* The account handles are the exception: a printed report should not show
     chrome for a link nobody can click on paper, so they print as the figures
     around them do. */
  a.cell-handle { color: var(--ink); text-decoration: none; }

  /* Nothing on the page should animate into a PDF render. */
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
}
