.panel {
    background: var(--panel);
    border: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.panel-left { width: 33.333%; min-width:0; max-width:none; flex:1 1 0; }
.panel-right { width: 33.333%; min-width:0; max-width:none; flex:1 1 0; }

.panel-header {
    padding: 10px 14px;
    background: var(--panel2);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    cursor: pointer;
}
.panel-header h3 {
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--accent);
}
.panel-body {
    flex: 1;
    overflow-y: auto;
    padding: 10px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.tabs {
    display: flex;
    gap: 0;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
    flex-wrap: wrap;
}
.tab {
    padding: 8px 14px;
    cursor: pointer;
    font-size: 11px;
    font-weight: 500;
    color: var(--text2);
    border-bottom: 2px solid transparent;
    transition: all 0.2s;
    white-space: nowrap;
    letter-spacing: 0.3px;
}
.tab:hover {
    color: var(--text);
    background: rgba(74, 158, 255, 0.05);
}
.tab.active {
    color: var(--accent);
    border-bottom-color: var(--accent);
    background: rgba(74, 158, 255, 0.08);
}
.tab-content {
    display: none;
    padding: 10px 0;
    flex: 1;
    overflow-y: auto;
}
.tab-content.active { display: block; }

/* Tables inside panels */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 11px;
    margin-bottom: 8px;
}
.data-table th {
    background: var(--panel2);
    color: var(--accent);
    padding: 6px 6px;
    text-align: center;
    font-weight: 600;
    font-size: 10px;
    letter-spacing: 0.3px;
    text-transform: uppercase;
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 2;
}
.data-table td {
    padding: 5px 6px;
    border-bottom: 1px solid rgba(42, 42, 90, 0.4);
    color: var(--text);
}
.data-table tr:hover td { background: rgba(74, 158, 255, 0.04); }
.data-table input, .data-table select {
    background: var(--panel2);
    border: 1px solid var(--border);
    color: var(--text);
    padding: 3px 5px;
    border-radius: 3px;
    font-size: 11px;
    width: 100%;
    font-family: var(--mono);
}
.data-table input:focus, .data-table select:focus {
    outline: none;
    border-color: var(--accent);
}
.data-table .num { text-align: center; font-family: var(--mono); }
.data-table .num input { text-align: center; }
.data-table .remove-btn {
    color: var(--danger);
    cursor: pointer;
    font-size: 14px;
    text-align: center;
    width: 24px;
    padding: 0;
}
.data-table .remove-btn:hover {
    background: rgba(255, 74, 106, 0.2);
    border-radius: 3px;
}

/* ---- Touch ----------------------------------------------------------------
   MEASURED at 390px: the row-delete control rendered 24x14. It sets a `width`
   and no height, so the 14px is a line-height accident - a 3.5mm target sitting
   between the row you meant to delete and the one you delete instead.

   34px, not 44px: this control sits inside a data-table row, and a 44px target
   would double every row's height and push the rest of the table off screen.
   34px clears WCAG 2.5.8 AA (24px) with room, which is the correct trade for a
   repeated in-row control. The toolbar in the header, which has room to grow,
   does get the full 44px. */
@media (pointer: coarse) {
    .data-table .remove-btn {
        width: auto;
        min-width: 34px;
        min-height: 34px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    /* The shear toggle is a full-width label wrapping a 13x13 checkbox, so the
       label is already the target - it just had no minimum height, and its box
       was being measured as 13px because the checkbox is what carries a box. */
    .sw-toggle { min-height: 44px; padding: 6px 0; }
}

/* Model Input → Loads: centre-justify every cell and its controls so the
   table reads as a tidy spreadsheet (headers are already centred). */
#loads-table td {
    text-align: center;
}
#loads-table td select,
#loads-table td input {
    text-align: center;
}
#loads-table td .num {
    text-align: center;
}

/* ==========================================================================
   BASE PLATES - the second, independently toggleable table in the Reactions tab.
   Lives in panel.css because these are panel components; index.html loads
   main.css BEFORE components/panel.css, so a rule defined in both would have
   main.css win silently. Only one definition exists, here.
   ========================================================================== */
.bp-head {
    margin: 10px 0 6px;
}
/* The control that shows/hides the base plate table is a TICKBOX. A push button
   that had to rewrite its own label between "Base plates" and "Hide base
   plates" was a control pretending to be something it is not; a real checkbox
   also keeps its state in the DOM, so it survives the table re-rendering
   underneath it and needs no aria-expanded bookkeeping. */
.bp-tick {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--text);
    cursor: pointer;
    user-select: none;
}
.bp-tick input {
    margin: 0;
    cursor: pointer;
}
.bp-tick:focus-within {
    outline: 1px solid var(--accent);
    outline-offset: 2px;
}
.bp-empty,
.bp-skipped {
    font-size: 11px;
    color: var(--text2);
    margin: 8px 0;
}
.bp-skipped ul {
    margin: 4px 0 0 16px;
    padding: 0;
}
.bp-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 6px;
}
/* THE INPUT ROW IS A ROW OF FIELDS, NOT A ROW OF LOOSE TEXT.
   The label/control pairs used to be inlined with no wrapper, so each field was
   exactly as wide as its own label: "f'c" pinned its input to the edge of the
   bar while "Method" left one adrift in the middle, and the four inputs had no
   shared left edge or right edge for the eye to line up. A field is a flex item
   with a FIXED label column and a fixed control column, so every input starts
   at the same x. Both widths are minimums, so a longer label or a wider select
   grows the column rather than pushing the row out of alignment. */
.bp-fields {
    display: flex;
    align-items: center;
    gap: 8px 14px;
    flex-wrap: wrap;
}
.bp-field {
    display: flex;
    align-items: center;
    gap: 6px;
}
.bp-lbl {
    font-size: 11px;
    color: var(--text2);
    /* RIGHT-ALIGNED in a fixed column. Left-aligning it would make the gap
       between a label and its own input vary with the label's length, which is
       the raggedness being fixed. */
    min-width: 52px;
    text-align: right;
    flex: none;
    white-space: nowrap;
}
.bp-u {
    font-size: 10px;
    color: var(--text2);
    white-space: nowrap;
}
/* THE INPUTS LOOK LIKE THE REST OF THE SITE. They did not: .bp-inp set only a
   width, a font size and padding, so background, border and text colour all
   fell through to the BROWSER default - a white box with black text in the
   middle of a dark panel, and a select that rendered in the OS chrome. The
   site's own form look is one declaration wide and is already written twice
   (main.css .data-table input, modal.css .form-group input); this is the same
   thing a third time, not a new style. */
/* No `.bp-lbl:first-child { margin-left: 0 }` any more. The gap between a
   label and its own input is now `.bp-field`'s, and the leading label no longer
   hangs off the left edge of a pill because there is no pill - so the rule
   that cancelled the margin was cancelling nothing and hiding the real spacing.
   It is also why the row used to look left-heavy: the first field had no inset
   and every one after it had 8 px. */
.bp-inp {
    width: 78px;
    background: var(--panel2);
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: 3px;
    padding: 3px 5px;
    font-size: 11px;
    font-family: var(--mono);
}
.bp-inp:hover {
    border-color: var(--accent);
}
.bp-inp:focus {
    outline: none;
    border-color: var(--accent);
}
/* The two selects are given a width rather than `auto`, because `auto` is what
   let them be two different widths - "Elastic"/"Plastic" and a grade like
   "4.6/TB" - and a control column of uneven width defeats the alignment the
   field wrapper exists to create. `auto` is kept as a MINIMUM by the field's
   own min-width, so a longer option list still widens it rather than clipping.
   A select ignores `width` on some platforms without this box-sizing. */
select.bp-inp {
    width: 92px;
    box-sizing: border-box;
    font-family: inherit;
    cursor: pointer;
}
/* ONE generic sketch, sitting in the header row of the base plate table.
   Both it and the column headings freeze for vertical scrolling: the sketch
   row is the sticky top row (top: 0) and the headings stack underneath it at
   --bp-sketch-h, the height legendSvg generated for the drawing, which the
   table writes onto the element. Two sticky rows that both sat at top: 0 would
   slide over one another instead of stacking. */
.bp-sketch-row th {
    top: 0;
    z-index: 3;
    padding: 4px 6px 6px;
    line-height: normal;
    white-space: normal;
    vertical-align: middle;
}
.bp thead tr.bp-head-row th {
    top: var(--bp-sketch-h, 0px);
}

/* ---- Phone: the frozen header was TALLER THAN THE PANE ------------------
   The two rules above build a FROZEN header of sketch + headings. On a
   desktop panel that is affordable. In the stacked phone layout the results
   pane is capped at 42dvh (main.css), and the two together do not fit in it:

     scrollport height : 334px
     frozen header     : 310px   (sketch 369 scaled + the heading row)
     usable band       :  25px
     row0: 0px of 56px visible
     row1: 0px of 67px visible

   MEASURED on the deployed build at 390x844, scrolled to the bottom. A row
   can only be seen in the band between the bottom of the frozen header and
   the bottom of the scrollport, and that band is 25px against rows of 56px
   and 67px - so NEITHER row is ever visible at any scroll position. The user
   sees the drawing and the column headings and no data, which is exactly the
   reported symptom, and it is invisible on desktop because a desktop panel is
   taller than 310px.

   The fix is to freeze only what is small. The drawing is a picture, not a
   column header: on a phone it scrolls away and the headings keep the top.
   --bp-sketch-h is what pushed the heading row down to 310px, so it has to go
   to 0 with the sketch, or the headings would float at the old offset over
   rows. Net effect: a 50px frozen header in a 334px pane, leaving 284px for
   the results.
   -------------------------------------------------------------------- */
@media (max-width: 900px) {
    /* These selectors carry `thead` and `tr` deliberately. The base rule in
       main.css is `.data-table thead th` = (0,1,2), which BEATS a plain
       `.bp-sketch-row th` = (0,1,1) - so the first version of this rule set
       `position: static` and was silently ignored, and the sketch row stayed
       sticky. A CSS rule that loses on specificity does not fail loudly; it
       simply does not apply. */
    .data-table thead tr.bp-sketch-row th {
        position: static;
        top: auto;
    }
    .data-table thead tr.bp-head-row th {
        top: 0;
    }
    /* And the drawing itself stops being taller than the pane it lives in, so
       it is one scroll away rather than a full screen of scrolling. The SVG
       carries its own viewBox, so constraining the box scales it evenly. */
    .bp-sketch-slot {
        max-height: 220px;
        overflow: hidden;
    }
    .bp-sketch-slot svg {
        max-width: 100%;
        max-height: 220px;
        height: auto;
    }
}
.bp-sketch-cap {
    font-size: 10px;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--text2);
    line-height: 1.35;
    padding: 2px 0 0;
    text-align: center;
}
.bp-sketch {
    width: auto;
    max-width: 100%;
    height: auto;
}
/* UTILISATION, CAPACITY OVER DEMAND. 100% is "exactly adequate", so green is
   the LOW number here - a plate doing a quarter of the work it could is not
   the one that needs attention. The classes are on the number itself, so the
   reading is in the cell and does not depend on a legend elsewhere. */
.bp-util-ok { color: var(--success); font-weight: 600; }
.bp-util-warn { color: var(--warning); font-weight: 600; }
.bp-util-bad { color: var(--danger); font-weight: 700; }
/* No tension in the bolts at this eccentricity: there is no bolt check to
   report, and a ratio to a non-positive demand is not a number. */
.bp-util-na { color: var(--text2); }
/* The governing combination is a NAME, not a number, and it can be long. It
   gets its own room so it cannot push the numeric columns out of alignment. */
.bp-case {
    font-size: 10px;
    color: var(--text2);
    max-width: 190px;
}
.bp-row td {
    vertical-align: middle;
}
.bp-fail td {
    color: var(--danger);
    font-size: 10px;
    padding: 2px 8px 6px;
}
.bp-txt {
    font-size: 10px;
    padding: 2px 6px;
}
