.header {
    background: var(--panel);
    border-bottom: 1px solid var(--border);
    padding: 8px 16px;
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
    z-index: 10;
}
.header h1 {
    font-size: 18px;
    font-weight: 700;
    letter-spacing: 1px;
    background: linear-gradient(135deg, var(--accent), var(--accent2));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    white-space: nowrap;
}
.header .subtitle {
    font-size: 11px;
    color: var(--text2);
    white-space: nowrap;
    /* The subtitle is the one item in the bar allowed to give up width: it is
       branding, and a truncated "Structural Analysis &…" costs the user nothing
       a control cannot. So it is the one element that shrinks (four times
       faster than the toolbar) and ellipsises instead of pushing the buttons
       onto a second row. */
    min-width: 0;
    flex-shrink: 4;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- Toolbar: clusters, not a wall ------------------------------------
   One container holds the whole bar (so `margin-left:auto` still
   right-aligns the toolbar as a unit) and each group inside it is a
   cluster. The two gaps are deliberately different widths: buttons
   that belong together sit close, unrelated clusters are held apart
   by a rule. A single uniform gap is what made the bar read as one
   undifferentiated list.

   NOTE: styles/main.css carries a second, indented copy of these
   header/button rules. It loads first, so everything here wins on
   source order for equal specificity — but the copy is stale debt
   and a future edit made only there would silently do nothing. */
.header .btn-group {
    display: flex;
    align-items: center;
    gap: 7px;                 /* between clusters: a rule and room to breathe */
    margin-left: auto;
    flex-wrap: wrap;
    row-gap: 6px;
    /* NOT squeezed to fit. This element wraps its OWN children, so any
       compression of it — even by one pixel — drops a whole cluster onto a
       second row. Letting it shrink therefore does not make the toolbar
       smaller, it makes it taller; the slack belongs to the subtitle, which
       ellipsises instead. Released again in the media query below, where
       wrapping is the right answer. */
    flex-shrink: 0;
}
.header .btn-cluster {
    display: flex;
    align-items: center;
    gap: 4px;                 /* inside a cluster: related, so close */
}
/* Clear is pushed to the far end of whatever row it lands on, so it can never
   end up shoulder-to-shoulder with Save — not by design of the clusters, and
   not by accident of a narrow window wrapping the row at a bad point. The
   spacing does the saying when the labels run out of room. */
.header .btn-cluster[data-group="clear"] { margin-left: auto; }
.header .btn-div {
    width: 1px;
    height: 20px;
    /* --border on --panel is nearly invisible, which defeats the point of a
       rule: the gap alone was doing the work and the grouping read as a
       suggestion. --text2 at low alpha is a line you can actually see. */
    background: var(--text2);
    opacity: 0.3;
    flex-shrink: 0;
}
/* The rule in front of Clear is tinted, so the gap the eye reads
   before the destructive button is the one the styling calls out. */
.header .btn-div-danger { background: var(--danger); opacity: 0.75; }

/* Tighter buttons in the toolbar only: nine of them at the default 14px side
   padding cost width the bar does not have — measured, the bar needed 1438px
   and wrapped on any screen narrower than 1440. Panels and modals keep the
   roomier default. */
.header .btn { padding: 7px 8px; }

/* Below this the toolbar cannot share the row with the branding text at all
   (measured: the bar is 933px and the title block another ~200px, so the
   subtitle is down to a sliver). Two steps rather than one: the subtitle goes
   first, and only if that is still not enough is the bar allowed to compress —
   and compressing it means wrapping to a second row, with Clear pushed to the
   far end of whatever row it lands on rather than up against Save. */
@media (max-width: 1180px) {
    .header .subtitle { display: none; }
    .header .btn-group { flex-shrink: 1; }
}

/* ---- Phone: the toolbar was setting a 460px floor on the whole page ------
   MEASURED, and this is the defect that made the app unusable on a phone: at
   a 390px viewport the page reported a 461px layout width and Chrome scaled
   everything down to fit (a 844px-tall phone reported 996px). Every
   overflowing element was in this bar - `.btn-group` reaching right=460, its
   clusters, the buttons inside them.

   Why it happened, and why `flex-shrink` was not the answer: above, the
   toolbar is allowed to wrap its OWN children but is deliberately NOT squeezed
   (`.btn-group` is `flex-shrink: 0`), because compressing it by a pixel drops a
   whole cluster onto a second row - it makes the bar taller, not narrower.
   That is right on a desktop window. It is wrong on a phone, where the bar has
   to become NARROWER, not taller, and where `h1` is a nowrap block sitting
   beside it and taking ~130px the toolbar cannot reclaim.

   So below 640px the bar changes shape instead of being compressed: the title
   takes its own row, and the toolbar takes the next one as a single row that
   scrolls sideways. Nothing has a minimum page width any more, so the page's
   minimum width becomes the viewport - which is what removes the scaling. */
@media (max-width: 640px) {
    .header {
        flex-wrap: wrap;
        padding: 8px 10px;
        gap: 8px;
    }
    .header h1 {
        /* `flex: 1 0 100%` rather than `width: 100%`: the basis makes the
           title claim a whole row by itself even where wrapping would
           otherwise have squeezed it alongside the toolbar. */
        flex: 1 0 100%;
        font-size: 16px;
        letter-spacing: 0.5px;
    }
    .header .btn-group {
        /* Same basis trick for the toolbar, now on its own row. */
        flex: 1 0 100%;
        min-width: 0;
        /* `margin-left: auto` right-aligned the bar against the title; with
           the title on its own row it would push the whole toolbar to the
           right and reintroduce the overflow in a new form. */
        margin-left: 0;
        gap: 6px;
        /* ONE scrollable row, not four wrapped ones. Wrapping between clusters
           measured 240px of header on an 844px phone, because the bar holds
           four clusters and each is the unit that wraps. A 3D app cannot spend
           28% of the screen on its toolbar.
           The scrollport is what makes this safe: content wider than a
           scroll container does not widen its ancestors, so a ~900px toolbar
           can sit inside a 390px screen without returning the page to the
           scaled-down state it started in. `min-width: 0` is load-bearing for
           the same reason - a flex item's automatic minimum size is its
           min-content width, which here is the entire toolbar, and that would
           defeat the scrollport and bring the page-level overflow back. */
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
        /* No visible scrollbar: at 44px of button there is no vertical room,
           and an always-visible 6px bar would cost more than it explains. */
        scrollbar-width: none;
    }
    .header .btn-group::-webkit-scrollbar { display: none; }
    /* Fade the right edge so the row visibly CONTINUES. A row that simply
       stops at the screen edge reads as the whole toolbar. */
    .header .btn-group {
        mask-image: linear-gradient(to right, #000 88%, transparent 100%);
    }
    /* `flex: 0 0 auto` stops a cluster being squeezed as the row scrolls; a
       cluster is the unit that means something and it keeps its width. */
    .header .btn-cluster { gap: 6px; flex: 0 0 auto; }
    /* The 1px dividers are a grouping cue for a single horizontal row, and
       they still work on one row - but they sit between clusters at the far
       edge of a scrolling strip, where they read as a clipped artifact. The
       gap does the grouping. */
    .header .btn-div { display: none; }
}

/* ---- Touch targets ---------------------------------------------------------
   `pointer: coarse` rather than a width breakpoint: a narrow desktop window is
   still a mouse, and inflating its toolbar because the window is small would be
   wrong. This only fires on a device whose primary input is a finger.

   44px is WCAG 2.5.5 (AAA) and 24px is WCAG 2.5.8 (AA). The toolbar gets 44 -
   it is the top-level command surface and has room to grow. The dense viz
   grid below stays at 24 and is laid out in fewer columns to earn the space. */
@media (pointer: coarse) {
    .header .btn {
        min-height: 44px;
        padding: 10px 12px;
    }
}