/* Mimic-board panel — the redesign's first surface.
   One hue, chosen 2026-09-16, and it carries one meaning: this is live. It marks
   what you can set or what the panel is pointing at — crosshair, pressed control,
   slider, opened row, the bars inside the window — and never a value you read.
   Ink stays ink. Every data trace is monochrome. */

:root{
  /* panel surfaces, coolest at the back */
  --face:          #e9eaec;   /* the wall the panel is bolted to */
  --panel:         #f7f8f9;   /* the instrument face */
  --panel-sunk:    #eff1f2;   /* recessed areas: tables, readouts */
  --rule:          #c4c8cc;   /* engraved hairline */
  --rule-soft:     #dcdfe2;   /* secondary rule */
  --bus:           #b4b9be;   /* the wire between things */

  --ink:           #14181b;   /* engraved label, primary value */
  --ink-2:         #3d454b;   /* secondary value */
  --ink-3:         #6b737a;   /* unit, caption, axis */

  /* data ink: one ink, because demand and price sit in separate panels and never
     share a frame. This is what makes a colourless build honest rather than unfinished. */
  --trace:         #14181b;
  --band:          rgba(20, 24, 27, .10);
  --band-inner:    rgba(20, 24, 27, .18);
  --grid:          #d7dade;

  /* the live hue. One value, used only for state. */
  --acc:           #14527d;
  --acc-quiet:     rgba(20, 82, 125, .12);   /* the opened row */
  --cross:         #14527d;                  /* crosshair through both panels */
  --signal:        #14527d;                  /* threshold bars inside the window */

  /* The second hue, and the last one that gets a meaning: SOMETHING HERE IS WRONG.
     A stale model, a dead pipeline, an infeasible solve, lost load, a scenario the
     engine cannot run. It is not a severity scale — the words carry severity — and
     it never marks a value you read, exactly like --acc.
     Why one and not two: amber and red are 3.1 ΔE apart under deuteranopia, so a
     warn/error pair would be one colour to a red-green colour-blind reader, which
     is worse than no colour at all. Measured against --acc: 14.7 ΔE protan,
     23.6 normal; 7.1:1 on --panel. */
  --flag:          #9b2c1f;
  --flag-quiet:    rgba(155, 44, 31, .07);

  --focus:         #14527d;

  --u1: 4px;  --u2: 8px;  --u3: 12px; --u4: 16px;
  --u5: 24px; --u6: 32px; --u7: 48px;

  --sans: "Source Sans 3", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--face);
  color:var(--ink);
  font-family:var(--sans);
  font-size:15px;
  line-height:1.45;
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased;
  /* The footer sits at the bottom of the screen on every page, even a short one:
     Refael's call (Figma, Contact, 2026-09-21). The page is a column and <main>
     takes the spare height, so the footer is pushed down rather than following
     the content. A long page is unchanged - it was already at the bottom. */
  min-height:100vh;min-height:100dvh;
  display:flex;flex-direction:column;
}
body > main{flex:1 0 auto;width:100%}   /* auto side margins would otherwise shrink it to its content */
::selection{background:var(--ink);color:var(--panel)}
:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* ---- panel scaffold -------------------------------------------------- */
.rail{
  display:flex;align-items:baseline;gap:var(--u4);flex-wrap:wrap;
  padding:var(--u3) var(--u5);
  background:var(--panel);
  border-bottom:1px solid var(--rule);
}
.rail .mark{font-weight:700;letter-spacing:.01em;color:var(--ink);text-decoration:none}
.rail nav{display:flex;gap:var(--u4);margin-left:auto;flex-wrap:wrap}
.rail nav a{color:var(--ink-2);text-decoration:none;border-bottom:1px solid transparent;padding-bottom:2px}
.rail nav a:hover{border-bottom-color:var(--bus)}
.rail nav a[aria-current="page"]{color:var(--acc);border-bottom-color:var(--acc)}
/* a section you are inside, but not the page you are on */
.rail nav a[aria-current="true"]{color:var(--ink);border-bottom-color:var(--bus)}

/* second row: the pages of the section you are in. Recessed, so the two rows read
   as one instrument rather than two competing bars. */
.rail.sub{display:flex;gap:var(--u4);flex-wrap:wrap;background:var(--panel-sunk);
  padding:var(--u2) var(--u5);font-size:.9rem}
.rail.sub a{color:var(--ink-2);text-decoration:none;
  border-bottom:1px solid transparent;padding-bottom:2px}
.rail.sub a:hover{border-bottom-color:var(--bus)}
.rail.sub a[aria-current="page"]{color:var(--acc);border-bottom-color:var(--acc)}

@media (max-width:430px){
  .rail.sub{padding:var(--u2) var(--u4)}
}

/* Two container widths, not one.
   The sheet was capped at a flat 1200px, so a 1920 monitor used 62% of its width
   and a 2560 used 46%, and charts stopped growing long before the screen did. What
   was settled in August was subtler: charts take the full content width with no
   ceiling, while the things that get harder to read as they widen — tables, prose —
   cap and sit left inside it. */
/* One gap, not a dozen margins.
   The chrome above the first plot was 467px on a 1,440 laptop and 168px of it was
   spacing - more than the readout tiles - spread across margins that collapsed
   against each other in ways that did not match what the stylesheet said. A flex
   column with a single gap and no child margins makes the number visible: change
   the gap, change the chrome, and the plot gets what is left. */
.sheet{max-width:none;margin:0 auto;
  padding:var(--u4) clamp(var(--u4), 3.5vw, var(--u7)) var(--u6);
  container-type:inline-size;
  display:flex;flex-direction:column;gap:var(--u4);align-items:stretch}
.sheet > *{margin:0}
@media (max-width:430px){ .sheet{padding:var(--u4) var(--u4) var(--u6)} .rail{padding:var(--u3) var(--u4)} }

/* A box is as wide as what it holds.
   These caps used to sit on the TABLE - 900, or 1400 for .wide - while the PANEL
   around it stayed full width. That is where the dead space came from: a bordered
   box with a hole in its right-hand third, 424px of it at 1920 and 1,064px at 2560.
   A cap on the table also stretches the table to the cap, which spread seven short
   numeric columns across 1,400px and left 113-196px of air inside every cell.
   Both faults are the same mistake, so both have the same fix: the limit belongs on
   the panel, and the table then fills the panel exactly.
   .wide survives as a marker for tables whose columns need room before they wrap;
   it no longer sets a width. */
.rows-wrap, .rows-wrap.wide{max-width:none}
/* A panel whose content is a table is as wide as that table. Charts, prose and the
   opened-interval grid all use whatever width they are given, so they are excluded
   and stay full width. */
/* :not([hidden]) matters: the results page keeps a rules table hidden until a
   market rule fires, and a hidden table is still a table to :has(), so the panel
   fitted itself to a line of text and sat 377px wide under a 1,824px chart. */
.zone:has(.rows-wrap:not([hidden])):not(:has(.chart)):not(:has(.prose)):not(:has(.iv)){
  width:fit-content;max-width:100%}
/* The 48-row wrappers scroll; reserve the bar's width so the fitted panel does not
   change size when the scrollbar appears. */
.rows-wrap{scrollbar-gutter:stable}

/* ---- and then: spend the width that fitting gave back ------------------ */
/* Fitting the panels removes the hole inside the box, but on a wide monitor it
   moves the emptiness outside it: a 793px table panel under a 1,824px chart leaves
   a thousand pixels of bare page down the right. The width is real and the content
   is genuinely narrow, so the only honest way to use it is to put more content in
   the row - two fitted panels side by side rather than one and a void.
   Flex, not grid: these panels are all different widths by design, and a track
   sized for one of them would either clip or stretch the others. Only panels that
   have fitted themselves take part; everything with a chart, prose or a full-width
   control keeps its own row. */
@media (min-width: 1400px) {
  .sheet{flex-direction:row;flex-wrap:wrap;align-items:flex-start;align-content:flex-start}
  .sheet > *{flex:0 0 100%}
  .sheet > .zone:has(.rows-wrap:not([hidden])):not(:has(.chart)):not(:has(.prose)):not(:has(.iv)){
    flex:0 1 auto}
}

/* a bolted panel zone */
/* The chrome above the first plot measured 467px on a 1,440 laptop and 168px of
   it - more than the readout tiles - was pure gap. Space that says "these belong
   together" does not have to be 40px, and every pixel of it comes off the plot. */
.zone{background:var(--panel);border:1px solid var(--rule)}

.zone > h2{
  margin:0;padding:6px var(--u4);
  font-size:.74rem;font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);border-bottom:1px solid var(--rule-soft);
}
.zone > .body{padding:var(--u4)}
/* A box as wide as Refael drew it in Figma (Contact, 523px), not the full sheet. */
.zone.narrow{width:523px;max-width:100%}
.sheet > .zone.narrow{flex:0 0 auto}   /* the wide-screen row rule gives every child 100% */

/* ---- date + provenance ----------------------------------------------- */
.head{display:flex;align-items:flex-end;gap:var(--u5);flex-wrap:wrap}
.head h1, .head h2{margin:0;font-size:1.5rem;font-weight:600;letter-spacing:-.01em}
.head .date-in{display:flex;align-items:center;gap:var(--u2)}
label{font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
input[type=date],input[type=number],select{
  font:inherit;font-family:var(--mono);font-size:.9rem;
  padding:5px var(--u2);background:var(--panel);color:var(--ink);
  border:1px solid var(--rule);border-radius:0;
}
button{
  font:inherit;font-size:.85rem;padding:5px var(--u3);
  background:var(--panel);color:var(--ink);border:1px solid var(--rule);border-radius:0;cursor:pointer;
}
button:hover{background:var(--panel-sunk)}
button[aria-pressed="true"]{background:var(--acc);color:var(--panel);border-color:var(--acc)}
button:disabled{color:var(--ink-3);cursor:not-allowed}
.prov{margin:0;font-family:var(--mono);font-size:.76rem;color:var(--ink-3)}
.prov b{font-weight:500;color:var(--ink-2)}

/* ---- readout tiles ---------------------------------------------------- */
/* Reflow, don't step. A fixed four columns meant the tiles were 400px wide on a
   2560 screen and cramped at 900; the count now follows the room. The hairlines are
   the 1px grid gap showing the rule colour through, which removes the nth-child
   border arithmetic that had to be redone at every breakpoint — and works for three
   tiles or five. */
.tiles{display:grid;grid-template-columns:repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  gap:1px;background:var(--rule-soft);
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule-soft)}
.tile{padding:var(--u3) var(--u4);background:var(--panel)}
.tile .k{display:block;font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.tile .v{display:block;font-family:var(--mono);font-size:1.5rem;font-weight:500;line-height:1.2;margin-top:2px}
.tile .u{font-size:.72rem;color:var(--ink-3);margin-left:3px}
.tile .at{font-family:var(--mono);font-size:.76rem;color:var(--ink-3)}
/* A short screen spends a bigger share of itself on furniture, so the readouts get
   tighter there rather than the plot getting flatter. Same four numbers, same
   order, less air. */
/* A short screen spends a bigger share of itself on furniture, so on one the
   furniture gets tighter rather than the plot getting flatter. Same four readouts,
   same order, same information - less air around it. */
/* A short screen spends a bigger share of itself on furniture, so on one the
   furniture gets tighter rather than the plot getting flatter. Same readouts, same
   order, same information - less air around it. Two bands, because 690px and 790px
   of viewport are different problems. */
@media (max-height: 800px){
  .sheet{gap:var(--u3);padding-top:var(--u3)}
  .tile{padding:6px var(--u3)}
  .tile .v{font-size:1.15rem}
  .tile .k{font-size:.66rem}
  .zone > h2{padding:4px var(--u4)}
  .rail.sub{padding:6px var(--u4)}
}
@media (max-height: 700px){
  .sheet{gap:var(--u2)}
  .tile{padding:4px var(--u3)}
  .tile .at{font-size:.7rem}
}

/* A phone is the hardest case: 560px of furniture on a 640px screen leaves the
   plot nothing. The rail stops wrapping onto a second row, the readouts lose their
   sub-line, and the whole stack tightens - which is what buys the plot its screen. */
@media (max-width: 430px){
  .tiles{grid-template-columns:1fr 1fr}
  .tile{padding:5px var(--u3)}
  .tile .v{font-size:1.05rem}
  .tile .at{display:none}
  .sheet{gap:6px;padding-top:var(--u2)}
  .prov{font-size:.72rem;line-height:1.3}
  .head h1, .head h2{font-size:1.12rem}
  .rail.sub{padding:3px var(--u4)}
  /* the rail wrapped onto two tall rows and cost 99px of a 640px screen */
  .rail{padding:6px var(--u4);gap:6px var(--u3)}
  .rail nav{gap:var(--u3)}
  .rail nav a{font-size:.82rem}
  .rail.sub{padding:5px var(--u4);gap:var(--u3)}
  .rail.sub a{font-size:.8rem}
  /* title and its date control, tightened rather than stacked apart */
  .head{gap:var(--u2) var(--u3)}
  .head h1, .head h2{font-size:1.2rem}
  /* the plot's name and unit ARE the title; the legend is not, and it was wrapping
     onto a second line and costing 19px */
  .panel-head .legend{display:none}
}
@media (max-height: 700px){
  .sheet{gap:6px}
  .zone > h2{padding:2px var(--u4)}
  .tile{padding:3px var(--u3)}
}
/* On a phone a 210px floor means one tile per row, so four readouts cost 350px of
   a 640px screen and push the chart off the bottom before it is drawn. Two up:
   the same four numbers in half the height, and the row is full either way. */
@media (max-width:430px){
  .tiles{grid-template-columns:1fr 1fr}
  .tile{padding:var(--u2) var(--u3)}
  .tile .v{font-size:1.25rem}
}

/* ---- stacked trace panels -------------------------------------------- */
.trace-zone .body{padding:var(--u3) var(--u4) var(--u4)}
.panel-head{display:flex;align-items:baseline;gap:var(--u2);margin-bottom:var(--u1);flex-wrap:wrap}
.panel-head .name{font-size:.8rem;font-weight:600}
.panel-head .unit{font-family:var(--mono);font-size:.74rem;color:var(--ink-3)}
.panel-head .legend{margin-left:auto;font-family:var(--mono);font-size:.72rem;color:var(--ink-3)}
/* Height grows with width, so a wider screen opens the curve up instead of
   flattening it. At a fixed 190px a chart measured the same on a 1440 laptop and a
   2560 monitor — 5.9:1, a letterbox — and the shape of the day was the thing being
   squeezed out. 24cqi is 24% of the container's inline size, which keeps the aspect
   ratio roughly constant as the sheet widens, and 480px is the ceiling settled in
   August. Without a ceiling two stacked panels came to 1,100px on a 2560 screen and
   you could no longer see demand and price together, which is the point of them
   sharing a time axis. */
/* --chart-h is measured at runtime (see panel/shell.html): the viewport minus
   whatever chrome actually sits above the first chart, so one plot always fits
   without scrolling. It is measured rather than hard-coded because "the chrome
   above the chart" is 395px TODAY — add a warning banner and a constant would go
   stale and the fit would break quietly.
   The value here is the fallback for no-JS and for first paint, and the 24cqi term
   inside the runtime calculation is what stops a 2560 monitor turning the chart
   back into a letterbox. */
.chart{position:relative;height:var(--chart-h, clamp(220px, 24cqi, 480px))}
/* The variants keep their relation to the base rather than carrying their own
   clamps, so one measurement sizes every chart on the site. */
.chart.s{height:calc(var(--chart-h, clamp(220px, 24cqi, 480px)) * 0.66)}
.chart.l{height:calc(var(--chart-h, clamp(220px, 24cqi, 480px)) * 1.25)}
.chart.xl{height:calc(var(--chart-h, clamp(220px, 24cqi, 480px)) * 1.55)}

/* ---- plots: a heading and the chart it names -------------------------- */
/* A plot is one thing - a heading and the chart it names - so it can be measured as
   one thing. Plots stay STACKED and keep the full width of their panel: they share
   a time axis and are read down the page, one under the other. */
.plots{display:grid;grid-template-columns:1fr;gap:var(--u4)}
/* notes, provenance lines and controls belong to the whole group */
.plots > :not(.plot){grid-column:1/-1}
/* the hairline that used to separate the stacked panels */
.plots > .plot + .plot{padding-top:var(--u3);border-top:1px solid var(--rule-soft)}
@media (max-width:430px){ .plots{gap:var(--u3)} }
/* On a phone the measurement is not useful: the chrome is a bigger share of a short
   screen, and a chart sized to "what is left" would be a sliver. Fixed heights, as
   before. */
/* No phone exception any more: the runtime measurement clamps at 170px, which is
   what the fixed phone heights were, so one mechanism now sizes every chart on
   every screen instead of two that had to agree with each other. */

/* ---- opened interval -------------------------------------------------- */
.iv{display:grid;grid-template-columns:auto 1fr;gap:var(--u4);align-items:start}
/* grid items default to min-width:auto, so the driver table's min-content width
   pushed the page sideways as soon as it loaded */
.iv > *{min-width:0}
.drivers-wrap{overflow-x:auto}
@media (max-width:1024px){ .iv{grid-template-columns:1fr} }
.iv-step{display:flex;align-items:center;gap:var(--u2)}
.iv-step .t{font-family:var(--mono);font-size:1.25rem;min-width:5.5ch;text-align:center}
.q-row{display:flex;gap:var(--u4);flex-wrap:wrap;font-family:var(--mono);font-size:.82rem;color:var(--ink-2)}
.q-row b{color:var(--ink);font-weight:500}

/* driver bars: a value, its 90% range, drawn on one shared scale */
.drivers{width:100%;border-collapse:collapse;margin-top:var(--u3)}
.drivers th{
  font-size:.7rem;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3);
  font-weight:600;text-align:left;padding:0 var(--u2) var(--u1) 0;border-bottom:1px solid var(--rule-soft);
}
.drivers td{padding:var(--u1) var(--u2) var(--u1) 0;border-bottom:1px solid var(--rule-soft);vertical-align:middle}
.drivers .num{font-family:var(--mono);text-align:right;white-space:nowrap}
.drivers .track{position:relative;height:14px;min-width:120px;background:var(--panel-sunk);display:block}
/* the bar needs room to mean anything; under 640px the printed range carries it alone */
/* The driver bar has a minimum width but could not grow, so every pixel the table
   gained past its natural width piled up as a gap in front of the RANGE column -
   205px of it at 1920. When something stretches, the thing that absorbs the slack
   has to be the bar, never the space beside it. */
.drivers .track-cell{width:100%}
.drivers .track{width:100%;min-width:140px}
@media (max-width:430px){ .drivers .track-cell{display:none} }
.drivers .zero{position:absolute;top:0;bottom:0;width:1px;background:var(--bus)}
.drivers .rng{position:absolute;top:6px;height:2px;background:var(--band-inner)}
.drivers .pt{position:absolute;top:2px;width:2px;height:10px;background:var(--trace)}

/* ---- threshold ---------------------------------------------------------*/
.thr-ctl{display:flex;gap:var(--u4);flex-wrap:wrap;align-items:flex-end;margin-bottom:var(--u3)}
.thr-ctl .grp{display:flex;flex-direction:column;gap:3px;min-width:0}
.thr-ctl .win{display:flex;gap:var(--u2);align-items:center;flex-wrap:wrap;min-width:0}
.seg{display:flex}
.seg button{border-right-width:0}
.seg button:last-child{border-right-width:1px}
.thr-out{font-family:var(--mono);font-size:1.8rem;font-weight:500}
.thr-out .u{font-size:.9rem;color:var(--ink-3)}
/* the two window sliders share the row and give way before the page scrolls */
input[type=range]{accent-color:var(--acc);width:150px;max-width:38vw;min-width:96px}

/* ---- 48 rows -----------------------------------------------------------*/
.rows-wrap{max-height:var(--rows-h, 420px);overflow:auto;border-top:1px solid var(--rule-soft)}
table.rows{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:.8rem}
table.rows th{
  position:sticky;top:0;background:var(--panel);z-index:1;
  font-family:var(--sans);font-size:.68rem;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;text-align:right;padding:var(--u2) var(--u3);
  border-bottom:1px solid var(--rule);
}
table.rows th:first-child{text-align:left}
table.rows td{padding:3px var(--u3);text-align:right;border-bottom:1px solid var(--rule-soft);color:var(--ink-2)}
table.rows td:first-child{text-align:left;color:var(--ink);white-space:nowrap}
table.rows tr[aria-selected="true"] td{background:var(--acc-quiet);color:var(--ink)}
table.rows tbody tr:hover td{background:var(--panel-sunk)}

/* a scenario the engine cannot price: said here, before the number is read */
table.rows tr.unsupported td{color:var(--ink-3)}
.mark-flag{margin-left:var(--u2);font-family:var(--sans);font-size:.68rem;
  letter-spacing:.06em;text-transform:uppercase;color:var(--flag);
  border:1px solid var(--flag);padding:0 4px;white-space:nowrap}

/* ---- prose -------------------------------------------------------------
   Running text caps at a reading column; tables and charts inside a prose page
   still take the full panel. Two widths, not one. */
.prose{max-width:70ch}
.prose p{margin:0 0 var(--u3)}
.prose p:last-child{margin-bottom:0}
.prose ul{margin:0 0 var(--u3);padding-left:1.1em}
.prose li{margin:0 0 var(--u1)}
.prose b{font-weight:600}

/* ---- next steps ------------------------------------------------------- */
.next{display:flex;gap:var(--u5);flex-wrap:wrap;padding:var(--u3) var(--u4)}
.next a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--bus);padding-bottom:1px}
.next a:hover{border-bottom-color:var(--ink)}

/* ---- states ------------------------------------------------------------*/
.state{font-family:var(--mono);font-size:.8rem;color:var(--ink-3);padding:var(--u4)}
/* "nothing to show yet" stays quiet ink above; "this is wrong" takes the flag hue,
   so the two are told apart before either is read. */
.state[data-kind="error"]{color:var(--ink);border-left:2px solid var(--flag);
  background:var(--flag-quiet);padding-left:var(--u3)}

/* A notice bolted across the panel: the model is stale, the pipeline is dead, the
   scenario will not run. It may be shortened on a narrow screen but never dropped —
   a correctness warning is the one thing a small screen does not get to lose. */
.flag{display:flex;gap:var(--u3);align-items:baseline;flex-wrap:wrap;
  padding:var(--u2) var(--u5);background:var(--flag-quiet);
  border-bottom:1px solid var(--flag);font-size:.85rem;color:var(--ink)}
.flag .k{font-family:var(--mono);font-size:.72rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--flag);font-weight:600}
@media (max-width:430px){ .flag{padding:var(--u2) var(--u4)} }
[hidden]{display:none !important}

/* ---- calibration: 48 states as marks, filled where the band held ------ */
.cal{display:flex;align-items:center;gap:var(--u3);flex-wrap:wrap}
.cal-k{font-size:.8rem;font-weight:600;min-width:7ch}
.cal-marks{display:flex;gap:2px;flex:1 1 260px;min-width:0}
.cal-marks .m{flex:1 1 0;min-width:0;height:16px;border:1px solid var(--rule);background:var(--panel)}
.cal-marks .m.in{background:var(--ink);border-color:var(--ink)}
.cal-v{font-family:var(--mono);font-size:.78rem;color:var(--ink-3);white-space:nowrap}
.cal-v b{color:var(--ink);font-weight:500}


/* ---- battery: the trade, stated as a trade ---------------------------- */
.hd-sub{font-weight:400;color:var(--ink-3)}
.thr-ctl .in-row{display:flex;align-items:center;gap:var(--u2)}
.thr-ctl .uu{font-family:var(--mono);font-size:.76rem;color:var(--ink-3)}

.trades{display:grid;grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));gap:var(--u3)}
.trade{position:relative;padding:var(--u3);background:var(--panel-sunk);border:1px solid var(--rule-soft)}
/* Two different things were both wearing the accent border: the tool's answer and
   the card you are reading. One signal, two meanings, and with three cards on screen
   you could not tell which panel below belonged to which.
   The border now means exactly one thing — this is the trade the panels are showing.
   The tool's answer is carried by its own line of text instead. */
.trade.pick{box-shadow:inset 3px 0 0 var(--bus)}
.trade{cursor:pointer}
.trade:hover{background:var(--panel)}
.trade.sel{background:var(--panel);border-color:var(--acc);
  box-shadow:inset 3px 0 0 var(--acc), 0 0 0 1px var(--acc)}
.trade.sel .t-v{color:var(--ink)}
.trade:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.trade .t-n{position:absolute;top:var(--u2);right:var(--u3);font-family:var(--mono);
  font-size:.78rem;color:var(--ink-3)}
.trade .t-leg{display:block;font-family:var(--mono);font-size:.94rem;color:var(--ink-2)}
.trade .t-leg b{color:var(--ink);font-weight:500}
.trade .t-hold{display:block;font-family:var(--mono);font-size:.74rem;color:var(--ink-3);margin-top:2px}
.trade .t-v{display:block;font-family:var(--mono);font-size:1.5rem;font-weight:500;
  line-height:1.2;margin-top:var(--u2)}
.trade .t-v .u{font-size:.72rem;color:var(--ink-3);margin-left:3px}
.trade .t-k{display:block;font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.trade .t-row{display:block;font-family:var(--mono);font-size:.76rem;color:var(--ink-3);
  margin-top:var(--u2);padding-top:var(--u2);border-top:1px solid var(--rule-soft)}
.trade .t-row b{color:var(--ink);font-weight:500}
.trade .t-pick{display:block;font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;
  color:var(--acc);margin-top:var(--u2)}

/* ---- pair matrix: a recessed plate, scrolled inside its own frame ------ */
.hm-wrap{overflow-x:auto;background:var(--panel-sunk);border:1px solid var(--rule-soft);padding:var(--u2)}
.hm-wrap canvas{display:block}
.hm-key{display:flex;align-items:center;gap:var(--u3);flex-wrap:wrap;margin-top:var(--u3)}
/* The key's caption is a sentence, not a readout. .cal-v is nowrap so short values
   ("held 95.6%") never break mid-number, but here that made one unbreakable item
   wider than the panel and pushed the page sideways at 768, 820 and 1024. */
.hm-key .cal-v{white-space:normal;min-width:0}
.ramp{display:flex;gap:2px}
.ramp .sw{width:26px;height:14px;border:1px solid var(--rule)}
.ramp .sw.none{background:var(--panel)}
.zoom-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--u3);
  margin-top:var(--u2);flex-wrap:wrap}
.trade .t-role{display:block;font-size:.68rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);margin-bottom:var(--u2)}
/* The role names what the trade is (most spread / the knee / least risk); it is not
   a state, so it stays ink. The accent on this card is reserved for selection. */
.trade.sel .t-role{color:var(--acc)}

/* a long legend squeezed the panel head into three narrow columns on a phone;
   give it its own line there instead */
@media (max-width:768px){
  .panel-head .legend{margin-left:0;flex-basis:100%}
}

/* ---- table cells that take input (the auction builder) ---- */
input.cell{
  font:inherit;font-family:var(--mono);font-size:.82rem;text-align:right;
  width:8ch;max-width:100%;padding:3px var(--u1);
  background:var(--panel);color:var(--ink);border:1px solid var(--rule-soft);border-radius:0;
}
input.cell:hover{border-color:var(--rule)}
input.cell:focus{outline:2px solid var(--focus);outline-offset:1px}

/* a legend that shows the line instead of naming its dash pattern */
.panel-head .legend .lg{display:inline-flex;align-items:center;gap:5px;margin-left:var(--u3)}
.panel-head .legend .lg i{display:inline-block;width:22px;border-radius:0}


/* ---- footer -------------------------------------------------------------
   One line, one link. It is the only place contact appears in the chrome, which
   is what keeps it off the front door as a call to action. */
.foot{display:flex;align-items:baseline;gap:var(--u4);flex-wrap:wrap;
  padding:var(--u3) clamp(var(--u4), 3.5vw, var(--u7));
  border-top:1px solid var(--rule);background:var(--panel);
  font-size:.82rem;color:var(--ink-3)}
.foot a{color:var(--ink-2);text-decoration:none;
  border-bottom:1px solid var(--bus);padding-bottom:1px}
.foot a:hover{color:var(--ink);border-bottom-color:var(--ink)}


/* ---- touch targets ------------------------------------------------------
   Measured on the ten widths: at 430 and below the rail links were 21-25px high
   and the window sliders 16px. A 22px target is a miss waiting to happen, and
   every one of these is a link someone navigates with. Nothing moves on a mouse
   screen — the padding only appears where the pointer is a finger. */
@media (max-width:430px){
  .rail nav a, .rail.sub a, .rail .mark, .foot a, .next a{
    display:inline-flex;align-items:center;min-height:32px}
  input[type=range]{height:28px}
  table.rows td a{display:inline-block;padding:4px 0}
}

/* ---- what a phone drops -------------------------------------------------
   A seven-column table on a 360 screen is not a table, it is a smear. The rule
   agreed for small screens is that they may DROP content rather than merely
   reflow it, so the columns that carry the spread come off and the ones that
   carry the answer stay. The full table is a rotation or a wider screen away. */
/* Dropping "Demand p05" and "Price p05" also dropped the only words saying which
   column was which, leaving a phone header reading "Time | p50 | p50". The word
   comes back onto the column that survives. Declared before the media query, or the
   later unconditional rule wins at every width. */

/* ---- tabbed pages (Refael, 2026-09-21) ---------------------------------
   One screen per tab, no page scroll: the date bar and the tabs are the one thing
   that never moves. Behaviour lives in static/js/tabs.js. */
.topbar{display:flex;flex-direction:column;gap:var(--u3);
  position:sticky;top:0;z-index:5;background:var(--face)}
.topbar .head{align-items:baseline;row-gap:var(--u2)}
/* a topbar that carries inputs (Battery, Analysis) takes half a phone screen; there it scrolls away */
@media (max-width:600px){ .topbar.tall{position:static} }
.tabs{display:flex;gap:0;border-bottom:1px solid var(--rule);flex-wrap:wrap}
.tabs button{border:1px solid transparent;border-bottom:0;background:none;
  padding:6px var(--u4);margin-bottom:-1px;color:var(--ink-2);font-size:.9rem;cursor:pointer}
.tabs button:hover{background:var(--panel-sunk);color:var(--ink)}
.tabs button[aria-selected="true"]{background:var(--panel);color:var(--ink);font-weight:600;
  border-color:var(--rule);border-bottom:1px solid var(--panel)}
.tabpanel{display:flex;flex-direction:column;gap:var(--u4)}
.tabpanel[hidden]{display:none}

/* every row beside the opened one; the table scrolls inside its box */
.split{display:grid;grid-template-columns:auto minmax(0,1fr);gap:var(--u4);align-items:start}
.split > *{min-width:0}
.split .rows-wrap{max-height:var(--rows-h, 420px)}
.split .q-row{display:block}
table.rows tbody tr[data-i]{cursor:pointer}
@media (max-width:900px){ .split{grid-template-columns:1fr} }
/* two plots read side by side */
.pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--u4)}
@media (max-width:900px){ .pair{grid-template-columns:1fr} }

/* a note under a panel: how to read it */
.reading{margin:0;font-size:.85rem;color:var(--ink-2);max-width:80ch}

/* two-row table head: the group name once, its columns under it, a rule between groups */
/* two rows stick as one block: sticking each cell leaves a seam the rows show through */
table.rows:has(thead tr + tr){border-collapse:separate;border-spacing:0}
table.rows thead:has(tr + tr){position:sticky;top:0;z-index:2;background:var(--panel)}
table.rows thead:has(tr + tr) th{position:static}
table.rows th.grp{text-align:center;color:var(--ink-2);border-bottom:1px solid var(--rule-soft)}
table.rows .g{border-left:1px solid var(--rule)}
.sm-cell{display:none}

/* The hover readout: the main number is bold, the band is labelled as its range. */
.tip{position:absolute;pointer-events:none;z-index:4;background:var(--ink);color:var(--panel);
  padding:6px 10px;font-family:var(--mono);font-size:.74rem;line-height:1.35;white-space:nowrap;
  opacity:0;transition:opacity .08s}
.tip .tt{color:var(--bus);margin-bottom:2px}
.tip .md{font-size:1rem;font-weight:500}
.tip .md small, .tip .bd small{font-size:.7rem;color:var(--bus);margin-left:4px}
.tip .bd{color:var(--bus)}
.tip .lab{display:inline-block;width:10.5ch;color:var(--bus);font-size:.68rem;
  text-transform:uppercase;letter-spacing:.06em}

.sm-only{display:none}
@media (max-width:430px){
  table.rows .drop-sm{display:none}
  table.rows .sm-cell{display:table-cell}
  .sm-only{display:inline}
}

/* ---- the pricing chain --------------------------------------------------
   The one line that matters on an auction result: what the marginal cost was,
   what the market rules published, and what it would have been without the
   binding constraint. Read left to right like a calculation, because it is one. */
.chain{display:flex;align-items:flex-end;gap:var(--u4);flex-wrap:wrap}
.cstep{display:flex;flex-direction:column;gap:2px}
.cstep .ck{font-size:.7rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.cstep .cv{font-family:var(--mono);font-size:1.5rem;font-weight:500;line-height:1.1;
  color:var(--ink-2)}
.cstep .cv.em{color:var(--ink)}
.carrow{font-family:var(--mono);color:var(--ink-3);padding-bottom:4px}
@media (max-width:430px){ .cstep .cv{font-size:1.2rem} }


/* ---- a phone shows the numbers (Refael, 2026-09-21) -----------------------
   Below 768px a page with plots keeps its boxes and drops its plots and long
   tables; the line at the top says where the rest is. tabs.js hides the tabs
   that would be left empty. Pages without plots are untouched. */
.phone-note{display:none}
@media (max-width:767px){
  body:has(.chart) .phone-note{display:block;margin:0;padding:var(--u3) var(--u4);
    border-bottom:1px solid var(--rule);background:var(--panel);font-size:.85rem;color:var(--ink-2)}
  body:has(.chart) .zone:has(.chart),
  body:has(.chart) .tabpanel .zone:has(.rows-wrap){display:none}
}
