/* ============================================================================
   screens/reports.css - service performance reporting
   ----------------------------------------------------------------------------
   One column of panels under a card strip, on every viewport. The screen has no
   master/detail to arrange, so the layout question here is not "where does it
   go" but "what survives being printed", and that shapes the whole file:

   1. NUMBERS ARE TEXT, BARS ARE DECORATION. Every bar in this file sits next to
      a figure that is already written out, and every chart is followed by a
      table carrying the same values. Browsers drop backgrounds when printing,
      so a bar prints as nothing; because the number is already there, nothing
      is lost. This is also why no meaning is ever carried by colour alone.

   2. THE PLOT SCROLLS, THE PAGE DOES NOT. A year of weekly buckets does not fit
      a 390px phone at a readable width, so .rpt-plot is its own horizontal
      scroller with a minimum column width. The document itself never scrolls
      sideways at any breakpoint.

   3. A MISSING VALUE IS NOT A ZERO. A measured zero keeps a hairline bar; a
      week with nothing to judge draws .is-none, which is a dotted rule where
      the bar would have started. The two must never look the same, because the
      SLA series genuinely contains both.

   Everything shared with other screens (.panel, .card, .stat-bar, .tag, .pill,
   .fchip, .btn, .av, .note, .empty, .ui-dt, .ui-tabs, .ui-seg, .ui-meter) comes
   from components.css and mobile.css. This file only adds what exists nowhere
   else, declares no colour that is not a token from tokens.css, and needs no
   priority flag: every selector either names a class unique to this screen or
   outscores the shared rule it refines.

   Motion: reports.js rebuilds both panes on every render, so anything that
   should move is an animation that plays when its node is created, never a
   transition with nothing to transition from. The panels and the card strip
   ride base.css's .stagger envelope, which is why the partial carries that
   class rather than this file inventing a second one, and the bars grow with
   the one screen-local keyframe below. The only transitions here are on
   properties that really do change in place: the sort chevron when a direction
   flips, the focus ring on the date fields, and the card strip dimming while a
   reload is on the wire. Every duration is a --t-* token, so the
   reduced-motion block in base.css collapses all of it by collapsing those.
   ============================================================================ */


/* ---------------------------------------------------------------------------
   0. Keyframes - the screen-local one only

   Only `from` is written, so the end state is the element's own computed style
   and no transform is left behind to create a stacking context.
   --------------------------------------------------------------------------- */

/* A bar growing out of the axis. components.css already ships bar-grow, but
   that one is scaleX for the horizontal .stat-bar and would flatten these. */
@keyframes rpt-bar-grow{
  from{ transform: scaleY(0); }
}


/* ---------------------------------------------------------------------------
   1. Screen shell

   .content is the app's one desktop scroller and already supplies the gutter,
   so this only has to stack its children. No height and no overflow: a report
   is as tall as it is, and the panels must not grow inner scrollers of their
   own or a printed page would stop at the first fold.
   --------------------------------------------------------------------------- */
.rpt{
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

/* The section strip runs the full width of the column and scrolls inside its
   own track when the eight sections do not fit. */
.rpt-sections{
  min-width: 0;
}

/* The period row: the control on the left, the sentence and Refresh on the
   right, wrapping to two lines rather than squeezing either. It lives here and
   not in .topbar because .topbar is a fixed 64px row that cannot wrap.

   Named .rpt-period, not .rpt-bar: section 8 gives .rpt-bar to a single column
   of a chart, and that rule is later in the file, so a row wearing the same
   class was being collapsed to the 15px wide, zero height box a chart bar is
   supposed to be, and its presets, dates and Refresh spilled out behind the
   card strip. Two different things may not share one class name. */
.rpt-period{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  min-width: 0;
}


/* ---------------------------------------------------------------------------
   2. The reporting period control
   --------------------------------------------------------------------------- */
.rpt-range{
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  min-width: 0;
}

.rpt-presets{
  flex-wrap: wrap;
}

.rpt-dates{
  display: flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}

/* A caption and the field under it. On the desktop row the caption is off, so
   this column has one child and lays out exactly as the bare field did. */
.rpt-field{
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  min-width: 0;
}

/* The word "to" sits between the two fields on the desktop row, which says
   which end is which without a caption. The phone stacks them, and section 11
   turns this back on. */
.rpt-date-l{
  display: none;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
  line-height: 1.3;
}

/* Composed the way .tb-search is: the field drops its own outline and the
   wrapper puts the ring back on :focus-within, so the control is never
   focusable invisibly. */
.rpt-date{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-height: 36px;
  padding: 0 var(--s-2);
  border: 1px solid var(--gray-300);
  border-radius: var(--r-sm);
  background: var(--white);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}

.rpt-date:focus-within{
  border-color: var(--gold);
  box-shadow: var(--focus-ring);
}

.rpt-date .ic{
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--gray-500);
}

.rpt-date-in{
  width: 124px;
  min-width: 0;
  padding: var(--s-2) 0;
  border: 0;
  background: none;
  font-family: var(--f-ui);
  font-size: 12.5px;
  color: var(--ink);
}

.rpt-date-in:focus{
  outline: none;
}

.rpt-date-sep{
  flex: none;
  font-size: 12px;
  color: var(--muted);
}

.rpt-actions{
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex: none;
  flex-wrap: wrap;
}

.rpt-actions:empty{
  display: none;
}


/* ---------------------------------------------------------------------------
   3. Summary line

   Plain text plus one button. print.css hides the button and keeps the
   sentence, which is how a printed sheet still states the range it covers.
   --------------------------------------------------------------------------- */
.rpt-summary{
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  min-width: 0;
}

.rpt-range-text{
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  margin: 0;
  min-width: 0;
}

.rpt-range-text b{
  font-family: var(--f-ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--navy);
}


/* ---------------------------------------------------------------------------
   4. The card strip
   --------------------------------------------------------------------------- */
.rpt-cards{
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(208px, 1fr));
  gap: var(--s-3);
}

/* A skeleton or an empty state is one child that means "the whole strip", so it
   takes the whole row rather than sitting in the first cell. */
.rpt-cards > .ui-skel-list,
.rpt-cards > .ui-empty{
  grid-column: 1 / -1;
}

/* A reload keeps the old figures on screen. Fading them says they are being
   replaced without emptying the strip and jumping the layout. */
.rpt-cards.is-busy{
  opacity: .6;
  transition: opacity var(--t-fast);
}


/* ---------------------------------------------------------------------------
   5. The stage and its panels

   One column. A two column grid would halve the width available to the tables,
   which are the part of this screen people actually print.
   --------------------------------------------------------------------------- */
.rpt-stage{
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  min-width: 0;
}

.rpt-panel{
  min-width: 0;
  border-radius: var(--r-lg);
}

/* The panel body is a stack: a meter, then a figure grid, then a chart, then a
   table, each separated by one step. */
.rpt-panel-b{
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  overflow: visible;
}

.rpt-foot{
  margin: 0;
}


/* ---------------------------------------------------------------------------
   6. Figure grid - the atom every totals block is made of
   --------------------------------------------------------------------------- */
.rpt-figs{
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
  gap: var(--s-3);
}

.rpt-fig{
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  min-width: 0;
  padding: var(--s-3);
  border: 1px solid var(--gray-100);
  border-radius: var(--r-sm);
  background: var(--gray-50);
}

.rpt-fig-l{
  font-size: 11.5px;
  font-weight: 500;
  color: var(--muted);
  line-height: 1.3;
}

.rpt-fig-v{
  font-family: var(--f-disp);
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--navy);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.rpt-fig-h{
  margin: 0;
}


/* ---------------------------------------------------------------------------
   7. Distribution list - a label, its figure, and the share it holds

   The bar composes on components.css's .stat-bar, which owns the track, the
   height and the --fill custom property; the tones below are the only addition.
   Load order does the rest: this sheet comes after components.css, so a plain
   class is enough to repaint the fill without a priority flag.
   --------------------------------------------------------------------------- */
.rpt-dist{
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.rpt-dist-r{
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  min-width: 0;
}

.rpt-dist-h{
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
  min-width: 0;
}

.rpt-dist-l{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rpt-dist-l .ic{
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--gray-500);
}

.rpt-dist-v{
  flex: none;
  font-family: var(--f-disp);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--navy);
}

.rpt-dist-n{
  margin: 0;
}

/* The list supplies its own rhythm, so the bar drops the margin .stat-bar
   carries for the inline figures on the Email screen. display is restated
   because .stat-bar declares none of its own: as a <span> inside anything that
   is not a flex container it would stay inline and its 4px height would be
   ignored. */
.rpt-dist .stat-bar{
  display: block;
  margin-top: 0;
}

.rpt-fill--gold::after{ background: var(--gold); }
.rpt-fill--navy::after{ background: var(--navy); }
.rpt-fill--green::after{ background: var(--green); }
.rpt-fill--red::after{ background: var(--red); }
.rpt-fill--blue::after{ background: var(--blue); }
.rpt-fill--slate::after{ background: var(--gray-400); }


/* ---------------------------------------------------------------------------
   8. Bar chart

   The plot is aria-hidden and every chart is followed by the same numbers as a
   table, so this is decoration in the strict sense: remove it and nothing is
   lost but the shape.
   --------------------------------------------------------------------------- */
.rpt-chart{
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  margin: 0;
  min-width: 0;
}

.rpt-legend{
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 12px;
  color: var(--muted);
}

.rpt-legend-i{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
}

.rpt-key{
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--gray-300);
}

.rpt-key--gold{  background: var(--gold); }
.rpt-key--navy{  background: var(--navy); }
.rpt-key--green{ background: var(--green); }
.rpt-key--red{   background: var(--red); }
.rpt-key--blue{  background: var(--blue); }
.rpt-key--slate{ background: var(--gray-400); }

/* One column per bucket, and its own horizontal scroller: a year of weekly
   buckets cannot be read at 7px a column, and the document itself must never
   scroll sideways. --col-min is written by reports.js with
   setProperty for the charts whose labels are names rather than dates, the same
   way a screen script writes --i, and never as an inline style attribute. */
.rpt-plot{
  --plot-h: 170px;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(var(--col-min, 44px), 1fr);
  align-items: end;
  gap: var(--s-1);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--gray-200);
  overflow-x: auto;
  overscroll-behavior-x: contain;

  /* A scroller with no edge of its own says nothing: seven clients on a 390px
     phone draw three columns and stop at the panel wall, and the four still to
     the right are not hinted at, so nobody swipes. These four layers are the
     standing scroll shadow trick. The two white ones travel with the content
     (local) and park over the two neutral ones (scroll) whenever that end is
     reached, so a shadow shows on an edge only while there is something past
     it, and a chart narrow enough to fit shows none at all. */
  background:
    linear-gradient(to right, var(--white), transparent) 0 0 / 28px 100% no-repeat local,
    linear-gradient(to left, var(--white), transparent) 100% 0 / 28px 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, var(--gray-300), transparent) 0 0 / 14px 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, var(--gray-300), transparent) 100% 0 / 14px 100% no-repeat scroll;
}

.rpt-col{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}

.rpt-bars{
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: var(--s-1);
  width: 100%;
  height: var(--plot-h);
}

/* reports.js builds a fresh node for every bar on every render, so the
   entrance is an animation rather than a transition: a node that has only ever
   had one height has nothing to transition from. scaleY from the bottom edge,
   so the growth never triggers layout. */
.rpt-bar{
  flex: 1 1 auto;
  max-width: 15px;
  height: var(--h, 0%);
  border-radius: 2px 2px 0 0;
  background: var(--gray-300);
  transform-origin: bottom center;
  animation: rpt-bar-grow var(--t-slow) backwards;
}

.rpt-bar--gold{  background: var(--gold); }
.rpt-bar--navy{  background: var(--navy); }
.rpt-bar--green{ background: var(--green); }
.rpt-bar--red{   background: var(--red); }
.rpt-bar--blue{  background: var(--blue); }
.rpt-bar--slate{ background: var(--gray-400); }

/* Nothing to measure. A dotted rule where the bar would have started, so it can
   never be mistaken for the hairline a measured zero keeps. */
.rpt-bar.is-none{
  height: 10px;
  border-top: 2px dotted var(--gray-400);
  border-radius: 0;
  background: none;
}

.rpt-col-l{
  max-width: 100%;
  font-size: 10.5px;
  color: var(--muted);
  line-height: 1.2;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rpt-chart-cap{
  margin: 0;
}

/* The series picker above the weekly SLA chart, inside the panel header. */
.rpt-sla-pick{
  display: flex;
  min-width: 0;
}


/* ---------------------------------------------------------------------------
   9. Table trimmings
   --------------------------------------------------------------------------- */
.rpt-sorts{
  flex-wrap: wrap;
}

/* One chevron, turned over for ascending: the sprite has no chevron-up, and
   adding one would mean editing a file five other screens share. */
.rpt-dir{
  width: 14px;
  height: 14px;
  transition: transform var(--t-fast);
}

.rpt-dir--up{
  transform: rotate(180deg);
}

.rpt-who{
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  min-width: 0;
}

.rpt-who b{
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A status word that keeps its meaning in greyscale: the label is spelled out,
   and the ground is only a second signal. The base keeps .tag's 1.4 line box
   from growing a dense table row; the tones are the only colour added. */
.rpt-tag{
  line-height: 1.2;
}

.rpt-tag--green{ background: var(--green-100); color: var(--green-700); }
.rpt-tag--gold{  background: var(--gold-100);  color: var(--gold-700); }
.rpt-tag--red{   background: var(--red-100);   color: var(--red-700); }
.rpt-tag--blue{  background: var(--blue-100);  color: var(--navy); }
.rpt-tag--navy{  background: var(--gray-100);  color: var(--navy); }
.rpt-tag--slate{ background: var(--gray-100);  color: var(--gray-600); }

/* The pager sits under its table, aligned with the rest of the panel body. */
.rpt-panel-b > .ui-pager{
  align-self: flex-start;
}


/* ---------------------------------------------------------------------------
   10. Detail dialogs
   --------------------------------------------------------------------------- */
.rpt-detail{
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
  min-width: 0;
}

.rpt-detail-tags{
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin: 0;
}


/* ---------------------------------------------------------------------------
   11. Mobile

   The app-wide shell breakpoint. Below it the mobile pane is the one on screen,
   so the period control becomes a card of its own inside the body and every
   control grows to the 44px touch floor from contract section 4.5.

   Four things change shape here, all for the same reason: a 390px line cannot
   hold a row that a 1440px one holds comfortably, and nothing on this screen
   may be reachable only by a gesture that leaves no mark on screen.

     the section strip   reports.js draws chips into the mobile slot instead of
                         the tab rail, and they wrap to a second line rather
                         than scrolling four sections out of sight
     the date fields     stacked, each under its own caption
     the action row      breaks after two, so no label is squeezed
     the card strip      one column, and the figure moves up beside its label
   --------------------------------------------------------------------------- */
@media (max-width: 860px){
  .rpt-mbody{
    gap: var(--s-4);
  }

  .rpt-range--card{
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-3);
    padding: var(--s-3);
  }

  /* Two date fields and the word "to" wedged between them leave three cramped
     targets on one 390px line. Stacked, each field gets the whole width, and
     the caption that was decoration on the desktop row becomes the only thing
     saying which end of the range it is. */
  .rpt-range--card .rpt-dates{
    flex-direction: column;
    align-items: stretch;
    gap: var(--s-3);
  }

  .rpt-range--card .rpt-date-l{
    display: block;
  }

  .rpt-range--card .rpt-date-sep{
    display: none;
  }

  .rpt-range--card .rpt-date{
    flex: none;
    min-height: 44px;
  }

  /* The label around it is already 44px tall, but the field is what a thumb
     aims at, so the field itself carries the floor rather than sitting as a
     36px strip inside a taller box. */
  .rpt-range--card .rpt-date-in{
    width: 100%;
    min-height: 44px;
  }

  .rpt-presets .fchip,
  .rpt-msections .fchip{
    min-height: 44px;
  }

  /* Eight sections, and the head is not the screen. The tighter side padding
     is what lets the strip settle on two lines instead of three, and the 44px
     floor above still owns the height, so the target never shrinks with it. */
  .rpt-msections .fchip{
    padding-left: var(--s-2);
    padding-right: var(--s-2);
  }

  /* Three ghost buttons on one line leave each about 110px, which is a label
     with no room around it. A 140px basis lets the row break instead, so the
     two live actions share the first line at a comfortable width and the one
     that has no endpoint yet drops below them. */
  .rpt-actions .btn{
    flex: 1 1 140px;
    justify-content: center;
    min-height: 44px;
  }

  /* Save view has no endpoint behind it. Left to grow it would take the whole
     second line and read as the loudest thing on the screen, so it keeps its
     own width and the two live actions keep the emphasis. */
  .rpt-actions .btn:disabled{
    flex: 0 1 auto;
  }

  .rpt-panel{
    border-radius: var(--r-lg);
  }

  /* One column. Two 148px cells on a 390px screen break "SLA compliance" over
     two lines and run the sentence under it to four, which leaves the cards in
     a row at different heights and the strip looking like a broken comb. Full
     width, every card is a label, a figure and one or two lines of hint, and
     the row heights stop mattering because there is only ever one card in a
     row. */
  .rpt-cards{
    grid-template-columns: 1fr;
  }

  /* A full width card with the label stacked on top of the figure leaves the
     right half of itself empty and makes the strip taller than it needs to be.
     The panels below already read a figure the other way round, label on the
     left and value on the right (.rpt-dist-h), so the strip borrows the
     grammar this screen already has rather than inventing a second one. The
     hint keeps the full width underneath. */
  .rpt-cards .ui-stat-b{
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: baseline;
    column-gap: var(--s-3);
  }

  .rpt-cards .ui-stat-l{
    grid-column: 1;
    grid-row: 1;
  }

  .rpt-cards .ui-stat-v{
    grid-column: 2;
    grid-row: 1;
  }

  .rpt-cards .ui-stat-h{
    grid-column: 1 / -1;
  }

  /* One figure to a row. Two 132px cells stretch every box in a row to the
     tallest of them, so a box holding "In repair" and a single digit stands as
     tall as the box beside it whose label wraps and carries a sentence, and the
     difference shows as empty ground, 52px in that pair alone. A row left with
     one figure gives away the other half as well. One to a row removes both,
     and it puts the grid in the same grammar as the card strip above it and the
     distribution rows below it: label on the left, figure on the right, hint
     under the two. */
  .rpt-figs{
    grid-template-columns: 1fr;
  }

  .rpt-fig{
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline;
    column-gap: var(--s-3);
  }

  .rpt-fig-l{
    grid-column: 1;
    grid-row: 1;
  }

  .rpt-fig-v{
    grid-column: 2;
    grid-row: 1;
    font-size: 17px;
  }

  .rpt-fig-h{
    grid-column: 1 / -1;
  }

  /* A shorter plot leaves the table under it reachable without a long scroll. */
  .rpt-plot{
    --plot-h: 132px;
  }

  .rpt-summary{
    justify-content: space-between;
  }

  /* Refresh is an action like the three below it, and the 44px floor is about
     thumbs, not about which row a control happens to sit in. */
  .rpt-summary .btn{
    min-height: 44px;
  }
}


/* ---------------------------------------------------------------------------
   12. Print

   The point of this screen on paper is the figures and the tables. Four things
   have to be said out loud for that to work.
   --------------------------------------------------------------------------- */
@media print{
  /* A bar is a background, and browsers drop backgrounds when printing, so a
     printed chart is a row of empty boxes. Every chart on this screen is
     followed by a table carrying the same numbers, so the chart goes and
     nothing is lost. Same for the distribution bars and the meter track: the
     value next to each one is already written out as text. */
  .app .rpt-chart,
  .app .rpt-dist .stat-bar,
  .app .rpt-panel .ui-meter-track{
    display: none;
  }

  /* Chrome: the period picker, the section strip and the action row. The date
     fields are inputs rather than buttons, so print.css does not reach them and
     they would otherwise print as two empty boxes. The summary sentence above
     carries the range in words. */
  .app .rpt-range,
  .app .rpt-sections,
  .app .rpt-actions,
  .app .rpt-sorts,
  .app .rpt-sla-pick{
    display: none;
  }

  /* print.css hides every button inside .app, which is right for the chips and
     wrong for a table whose first column IS a button: ui.dataTable wraps the
     title cell in .ui-dt-open whenever a row opens a dialog, so the client and
     campaign columns would print empty. 0,3,0 beats that rule's 0,1,1. */
  .app .rpt .ui-dt-open{
    display: inline;
  }

  /* print.css keeps a .panel whole across a page break, which is right for a
     case card and wrong for a table of sixty contracts: a panel taller than the
     sheet would be pushed to a page it still cannot fit on. 0,3,0 beats the
     0,2,0 of that rule, which is what lets a long report paginate. */
  .app .rpt .rpt-panel{
    break-inside: auto;
  }

  /* The table's own scroller has no meaning on paper, and a scroll container
     prints only what is inside its box. */
  .app .rpt .ui-dt-scroll{
    overflow: visible;
  }

  /* Tinted grounds cost toner and say nothing here: the label and the figure
     are already black on white. */
  .app .rpt-fig{
    background: none;
    border-color: var(--gray-300);
  }

  .app .rpt-cards{
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-3);
  }

  /* Section 11 has no media type, so it matches an A4 sheet too: at 96dpi the
     page box is about 794px, well under the 860px shell breakpoint. Three
     cards to a printed row have no width for a label and a figure side by
     side, so the tile goes back to the stacked form components.css gives it. */
  .app .rpt-cards .ui-stat-b{
    display: flex;
    align-items: normal;
    column-gap: normal;
  }

  /* Section 11 matches an A4 sheet too, and one figure to a printed row wastes
     most of the width. On paper the grid takes as many columns as fit and each
     figure goes back to the stacked form section 6 gives it. */
  .app .rpt-figs{
    grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
  }

  .app .rpt-fig{
    display: flex;
    align-items: normal;
    column-gap: normal;
  }

  /* An entrance caught at its first frame would print blank. */
  .app .rpt-stage > *,
  .app .rpt-cards > *,
  .app .rpt-dist > *{
    animation: none;
  }
}
