/* Printable-parts design system: component styles, all values from tokens. The fonts load once, through tokens.css. */
/* The page: class ws-page on <body>. The browser's margin and its own fonts for controls give way to the tokens */
.ws-page{margin:0;color:var(--ink);background:var(--surface);font:var(--text-body)}
:where(.ws-page) :where(button,input,select,textarea){font:inherit}
/* The hidden attribute always hides (D57). The browser's own [hidden]{display:none} is the weakest rule there
  is, so every class here that sets display beats it. !important, as nothing
  else can beat the specificity of a rule such as the header's version; hidden=until-found keeps the
  browser's meaning, a section that find-in-page opens */
[hidden]:not([hidden=until-found i]){display:none !important}
.ws-text{font:var(--text-body);color:var(--ink);background:var(--surface);max-width:var(--measure);margin:0 auto;padding:var(--space-6)}
.ws-fieldset{border:var(--line) solid var(--border);border-radius:var(--radius-md);background:var(--surface-raised);margin:0 0 var(--space-4);padding:var(--space-3) var(--space-4) var(--space-4);min-width:0}
.ws-fieldset legend{font:var(--text-legend);padding:0 var(--space-1)}
/* Rhythm: the first item under the legend sits at the fieldset's padding, space-3, whatever its own top
  margin; a note sits space-1 under the group it explains, so groups keep no bottom margin */
.ws-fieldset > legend + *{margin-top:0}
.ws-label{display:block;margin-top:var(--space-3);font:var(--text-body)}
/* A field is exactly control-h tall: its block padding is what control-h leaves of its line, control-pad-y,
  less its border, as a Disclosure summary's is control-pad-y (D68); min-height keeps it so where a browser
  cannot read lh */
.ws-input{display:block;min-height:var(--control-h);font:inherit;color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border-strong);border-radius:var(--radius-sm);padding:calc(var(--control-pad-y) - var(--line)) var(--field-pad-x);margin-top:var(--space-1);width:100%;box-sizing:border-box}
.ws-input[aria-invalid=true]{border-color:var(--error);outline:var(--ring) solid var(--error)}
.ws-input:disabled{opacity:var(--opacity-disabled)}
/* A number field is as wide as a number, not as the sidebar */
.ws-input:is([type=number],[inputmode=decimal]){max-width:var(--number-field-w)}
.ws-input:focus-visible,.ws-btn:focus-visible,.ws-piece:focus-visible,.ws-summary:focus-visible,.ws-tile:focus-visible,.ws-rail__btn:focus-visible{outline:var(--ring) solid var(--action);outline-offset:var(--ring)}
.ws-note{color:var(--ink-muted);font:var(--text-note);margin:var(--space-1) 0 0}
.ws-error{color:var(--error);display:block;font:var(--text-note);min-height:1lh}
.ws-caps{font:var(--text-label-caps);letter-spacing:var(--text-label-caps-tracking);text-transform:uppercase;color:var(--ink-muted)}
.ws-btn{display:inline-flex;align-items:center;justify-content:center;white-space:nowrap;font:inherit;min-height:var(--control-h);padding:0 var(--space-4);color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border-strong);border-radius:var(--radius-sm);cursor:pointer}
/* Hover tints what can be used; a disabled control stays as it is, so it never looks usable. A button that
  must keep its focus while it cannot be used, a Stepper's at a limit, says so with aria-disabled and looks
  disabled: disabled, the focused button would lose its focus (Firefox drops it to the page, D56). A list,
  not :is(), so each selector keeps its own weight and the primary button's hover below still wins */
.ws-btn:hover:not(:disabled,[aria-disabled=true]),.ws-select > .ws-input:hover:not(:disabled,[aria-disabled=true]),.ws-summary:hover:not(:disabled,[aria-disabled=true]),.ws-piece:hover:not(:disabled,[aria-disabled=true]),.ws-tile:hover:not(:disabled,[aria-disabled=true]),.ws-rail__btn:hover:not(:disabled,[aria-disabled=true]),.ws-choice__input:hover:not(:disabled,[aria-disabled=true]) + .ws-choice__face,.ws-sides__btn:hover:not(:disabled,[aria-disabled=true]){background:var(--action-tint)}
/* Selected: tint, ring and an action border, in ink, as every selected state; only the primary button is
  filled (D26). The label is bold, but for a PieceMap cell's: there bold says a piece has its own fill, so
  the chosen radio's dot says it is selected (D47). Before a done step's rule, which keeps its border */
.ws-piece[aria-pressed=true],.ws-tile[aria-pressed=true],.ws-rail__btn[aria-current=step],.ws-choice__input:checked + .ws-choice__face,.ws-sides__btn[aria-pressed=true]{outline:var(--ring) solid var(--action);background:var(--action-tint);border-color:var(--action);color:var(--ink)}
.ws-tile[aria-pressed=true],.ws-rail__btn[aria-current=step],.ws-choice__input:checked + .ws-choice__face,.ws-sides__btn[aria-pressed=true]{font-weight:var(--weight-bold)}
.ws-btn--primary{background:var(--action);color:var(--on-action);border-color:var(--action);font-weight:var(--weight-bold)}
.ws-btn--primary:hover:not(:disabled,[aria-disabled=true]){background:var(--action);filter:brightness(.92)}
.ws-btn:is(:disabled,[aria-disabled=true]){opacity:var(--opacity-disabled);cursor:not-allowed}
.ws-btn--quiet{border-color:transparent;background:transparent}
/* Stepper (D56): a number field, then two Buttons, ↓ and ↑, in a row. The buttons are control-h tall, as the
  field is (D68), and stretch to the row, so they line up with it; they are at least control-h wide, a touch
  target each way on a touch screen; the field gives way on a narrow sidebar. The row keeps the space-1
  under the label that the field keeps on its own */
.ws-stepper{display:flex;align-items:stretch;gap:var(--space-1);margin-top:var(--space-1)}
.ws-stepper > .ws-input{flex:1 1 auto;min-width:0;margin-top:0}
.ws-stepper__btn{flex:none;min-width:var(--control-h);padding:0 var(--space-2)}
/* A type=number field in a Stepper keeps no spin buttons of its own beside the arrows: appearance:textfield
  hides them in Firefox, the pseudo-elements in Chromium and WebKit; neither alone hides all three (measured) */
.ws-stepper > .ws-input[type=number]{appearance:textfield}
.ws-stepper > .ws-input::-webkit-inner-spin-button,.ws-stepper > .ws-input::-webkit-outer-spin-button{appearance:none;margin:0}
/* Select (D69): the native select, a Field's input drawn from tokens, in a span.ws-select that draws its chevron.
  appearance:none takes the browser's own arrow away, which each engine draws its own way and WebKit at 42.5px
  tall (measured), and the chevron is a Codicon (chevron-down) from the font, so it is text: it takes ink, and in
  forced colours the user's text colour, where an image drawn with a background would vanish. It sits in a
  control-h square at the select's end, over the select, and lets a press through to it; the select keeps that
  square free of its text. Its alternative text is empty, so a screen reader skips it, as aria-hidden would.
  The list the browser opens follows the theme through the page's color-scheme */
.ws-select{display:grid;align-items:center;margin-top:var(--space-1)}
.ws-select > .ws-input{grid-area:1/1;margin-top:0;appearance:none;padding-inline-end:var(--control-h);cursor:pointer}
.ws-select::after{content:"\eab4";content:"\eab4" / "";grid-area:1/1;justify-self:end;width:var(--control-h);text-align:center;line-height:1;color:var(--ink);pointer-events:none}
.ws-select:has(> .ws-input:disabled)::after{opacity:var(--opacity-disabled)}
/* A row of buttons, as in the Button card; the plan bar and a dialog's menu have their own */
.ws-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2)}
/* Links in text: the accent colour, and the focus ring every control has */
:where(.ws-page,.ws-text,.ws-workbench) a{color:var(--action)}
:where(.ws-page,.ws-text,.ws-workbench) a:focus-visible{outline:var(--ring) solid var(--action);outline-offset:var(--ring)}
/* At least control-h tall (a touch target on touch screens), text centred; padding, not display, keeps the marker */
.ws-summary{cursor:pointer;font:var(--text-legend);padding-block:var(--control-pad-y)}
.ws-details{margin:0 0 var(--space-4)}
.ws-map{display:grid;gap:var(--space-1);margin:var(--space-2) 0 0}
.ws-piece{font:inherit;min-width:var(--tap);min-height:var(--control-h);padding:var(--space-1);color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border-strong);border-radius:var(--radius-sm);cursor:pointer}
/* Selected, a piece also shows the chosen radio's dot, as it is one of a pick-one (D47); the Choice rules
  below draw it. The cell's width comes from the grid's columns, so the dot never moves the map */
.ws-piece[aria-pressed=true]::before{display:inline-block;vertical-align:middle}
.ws-piece[data-own=yes]{font-weight:var(--weight-bold)}
/* A dialog keeps space-4 from the screen's edges (its max-width counts its padding and border), and the
  scrim behind it lifts it 3:1 off the page, by its face in light and its border-strong edge in dark (check-contrast.py) */
.ws-dialog{box-sizing:border-box;border:var(--line) solid var(--border-strong);border-radius:var(--radius-md);max-width:min(var(--dialog-w),calc(100vw - 2 * var(--space-4)));color:var(--ink);background:var(--surface-raised);padding:var(--space-5)}
.ws-dialog::backdrop{background:var(--scrim)}
.ws-dialog h2{font:var(--text-section);margin:0 0 var(--space-3)}
.ws-dialog menu{display:flex;gap:var(--space-3);justify-content:flex-end;margin:var(--space-5) 0 0;padding:0}
.ws-notice{border:var(--line) solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface-raised);padding:var(--space-3) var(--space-4);margin:var(--space-3) 0}
.ws-notice--error{border-color:var(--error)}
.ws-notice--warning{border-color:var(--warning)}
.ws-notice strong{display:block}
.ws-notice--error strong{color:var(--error)}
.ws-notice--warning strong{color:var(--warning)}
/* A Notice's place can be kept beside what it concerns (aria-live); empty, it collapses but stays in
  the page, so a screen reader announces it when it fills (display:none would take it out) */
.ws-notice:empty{padding:0;border-width:0;margin:0}
/* Gallery: a visual picker, a grid of tiles */
.ws-gallery{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-2);margin:var(--space-2) 0 0}
.ws-tile{display:grid;gap:var(--space-1);justify-items:center;padding:var(--space-2);color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border-strong);border-radius:var(--radius-sm);cursor:pointer;font:var(--text-label-caps)}
.ws-tile svg{width:100%;height:auto;display:block}
/* Workbench: header, step rail, sidebar, stage, status bar */
/* A phone: the stage lays its parts out in the page (display:contents), so the 3D view and the plan
  bar under it stay pinned while the status bar scrolls away with the form (D25) */
.ws-workbench{display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:var(--header-h) var(--stage-phone-h) auto auto 1fr;min-height:100svh;font:var(--text-body);color:var(--ink);background:var(--surface)}
.ws-header{grid-column:1/-1;box-sizing:border-box;display:flex;align-items:center;gap:var(--space-2);height:var(--header-h);padding:0 var(--space-3);background:var(--surface-raised);border-bottom:var(--line) solid var(--divider)}
/* One row on every screen. Below breakpoint-header the actions but Undo and Redo sit in a "More…" popover
  (D28), and check-previews.py fails if the row does not fit. Nothing clips it, so a focused button's
  ring shows whole */
.ws-header{white-space:nowrap}
.ws-header__actions:not(:popover-open){display:none}
/* Open, the More… and View… menus end at the screen's bottom edge and scroll inside: at 400 % zoom on a
  16:9 screen (320 × 180) neither fits under its button (D41) */
.ws-header__actions:popover-open{position:fixed;inset:var(--header-h) var(--space-2) auto auto;margin:0;display:flex;flex-direction:column;align-items:stretch;gap:var(--space-2);padding:var(--space-2);color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border);border-radius:var(--radius-md);box-sizing:border-box;max-height:calc(100svh - var(--header-h) - var(--space-2));overflow:auto}
/* Its items line up, their icons in one column, as a list reads (A86): a button centres its label */
.ws-header__actions:popover-open > .ws-btn{justify-content:flex-start}
/* Below breakpoint-header the header tightens to fit a phone: the name in the legend size, smaller gaps;
  narrower still, the name is cut short (its full text stays in the page) rather than the header scroll.
  breakpoint-header is wider than the layout's breakpoint, so the whole row waits until it fits
  beside a whole name (D49). These come before the wide-screen rules,
  which must win (build-tokens.py fails otherwise) */
.ws-header .ws-btn{padding:0 var(--space-2)}
.ws-header__name{font:var(--text-legend);margin:0 auto 0 0;min-width:0;overflow:hidden;text-overflow:ellipsis}
.ws-header__spacer{display:none}
/* Below breakpoint-header the version tag is in More…, after the actions, not in the row: beside the name it
  cuts a phone's name short (D51). The other copy is display:none, so a screen reader
  hears one version. A header without More… keeps its version in its row */
.ws-header:has(.ws-header__more) > .ws-header__version{display:none}
.ws-header__actions > .ws-header__version{text-align:center}
@media (min-width:64rem){
  .ws-header{gap:var(--space-3);padding:0 var(--space-4)}
  .ws-header .ws-btn{padding:0 var(--space-3)}
  .ws-header__name{font:var(--text-step);margin:0}
  .ws-header__spacer{display:block;flex:1}
  .ws-header__more{display:none}
  .ws-header__actions,.ws-header__actions:not(:popover-open){position:static;inset:auto;margin:0;display:flex;flex:none;overflow:visible;flex-direction:row;align-items:center;gap:var(--space-3);padding:0;background:transparent;border:0}
  .ws-header:has(.ws-header__more) > .ws-header__version{display:inline}
  .ws-header__actions > .ws-header__version{display:none}
}
.ws-header__version{font:var(--text-note);color:var(--ink-muted)}
.ws-stage{display:contents}
/* The view: the drawing behind, its own controls in a row over it, top right, so they flow and wrap
  rather than overlap (D30) */
.ws-stage__view{position:sticky;top:0;z-index:1;overflow:hidden;display:grid;grid-template-rows:auto minmax(0,1fr);gap:var(--space-2);min-height:0;padding:var(--space-2);background:var(--surface)}
.ws-stage__tools{grid-row:1;justify-self:end;z-index:1;display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--space-2)}
/* On a phone or a short screen: Reset view, and View…, a menu holding the zoom buttons and the Front,
  Back and Top choice, so the view stays clear for the fingers that pinch and drag; on a wide screen
  they are all on show (D36). Open, the menu sits below the view, over the plan bar, its right edge
  under View…'s, so the part stays in sight while zooming (D42); the tool's script closes it when a
  view is picked (Workbench README). Where a browser cannot anchor the menu, it opens mid-screen, over
  the part. align-content:start, or WebKit stretches the grid, and the menu, to the screen's height
  (bug 270334) */
.ws-stage__menu:not(:popover-open){display:none}
.ws-stage__menu:popover-open{display:grid;grid-template-columns:auto auto;justify-content:start;align-content:start;gap:var(--space-2);padding:var(--space-2);color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border);border-radius:var(--radius-md)}
.ws-stage__menu > .ws-stage__views{grid-column:1/-1;margin:0}
@supports (position-area:block-end){
  .ws-stage__view{anchor-name:--ws-stage-view}
  .ws-stage__menu:popover-open{position-anchor:--ws-stage-view;position-area:block-end span-inline-start;margin:var(--space-2) var(--space-2) 0 0;box-sizing:border-box;max-height:calc(100% - var(--space-2));overflow:auto}
}
/* Wide: the menu's contents join the row, the choice at the bottom left (D30), and View… goes */
@media (min-width:56rem){
  .ws-stage__more{display:none}
  .ws-stage__menu,.ws-stage__menu:not(:popover-open){display:contents}
  .ws-stage__menu > .ws-stage__views{position:absolute;left:var(--space-2);bottom:var(--space-2);z-index:1}
}
/* Between the controls, and beside a wrapped row, a drag goes through to the drawing */
.ws-stage__tools,.ws-stage__views{pointer-events:none}
.ws-stage__tools > *,.ws-stage__views > *{pointer-events:auto}
/* The tool's canvas (or an SVG) fills the view and never sizes it, so a resize cannot feed back into itself */
.ws-stage__view > :is(canvas,svg){position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:none}
/* Focused, the arrow keys turn it (D30); the ring is drawn inside, as the view clips anything outside */
.ws-stage__view > :is(canvas,svg):focus-visible{outline:var(--ring) solid var(--action);outline-offset:calc(-2 * var(--ring))}
/* A message over the stage while the part loads or cannot be drawn; empty, it takes no room */
.ws-stage__message{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);max-width:calc(100% - 2 * var(--space-4));margin:0;padding:var(--space-2) var(--space-3);border-radius:var(--radius-sm);text-align:center;color:var(--ink-muted);background:var(--surface-raised)}
.ws-stage__message:empty{padding:0}
.ws-status{display:flex;flex-wrap:wrap;align-items:center;column-gap:var(--space-4);row-gap:0;justify-content:space-between;min-height:var(--status-h);padding:var(--space-1) var(--space-4);border-top:var(--line) solid var(--divider);background:var(--surface-raised);font:var(--text-label-caps);color:var(--ink-muted)}
.ws-plan{position:sticky;top:var(--stage-phone-h);z-index:1;display:flex;align-items:center;gap:var(--space-4);justify-content:space-between;padding:var(--space-3) var(--space-4);border-top:var(--line) solid var(--divider);background:var(--surface-raised)}
/* The plan bar is one row on every screen: the sentence, at most two lines (the height of the button),
  beside the primary button (D25, D79); the whole sentence stays in the page for a screen reader */
.ws-plan__line{flex:1 1 0;min-width:0;margin:0;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;overflow-wrap:anywhere}
.ws-plan__actions{display:flex;flex:none;gap:var(--space-2)}
/* Text for assistive tech only, such as a done step's "done": in the page, not on the screen. The bar is
  one row, so the primary button shows a short label, "Download...": the rest of it, in ws-plan__long, is
  hidden from view the same way, so its name stays whole, and the sentence keeps plan-line-min, which
  check-previews.py holds (D39, D75) */
.ws-plan__long,.ws-sr{position:absolute;width:var(--line);height:var(--line);overflow:hidden;clip-path:inset(50%);white-space:nowrap}
/* Pinned, the stage would hide a focused field: scroll the page so a field lands below it */
:root:has(.ws-workbench){scroll-padding-top:calc(var(--stage-phone-h) + var(--control-h) + 2 * var(--space-3) + var(--line))}
/* A short or zoomed screen pins nothing, and nor does a landscape one too narrow for side by side (D41);
  side by side, below, re-sets each of these */
@media (max-height:30rem),(min-aspect-ratio:4/3){
  .ws-workbench{grid-template-rows:var(--header-h) minmax(var(--stage-phone-h),auto) auto auto 1fr}
  .ws-stage__view{position:relative}
  .ws-plan{position:static}
  :root:has(.ws-workbench){scroll-padding-top:0}
}
/* Wide but short, the view's controls go back into View… (D36) */
@media (max-height:30rem){
  .ws-stage__more{display:inline-flex}
  .ws-stage__menu:not(:popover-open){display:none}
  .ws-stage__menu > .ws-stage__views{position:static}
}
.ws-side{display:flex;flex-direction:column;min-width:0;background:var(--surface-raised);border-left:var(--line) solid var(--divider);overflow:auto}
.ws-rail{display:flex;flex-wrap:wrap;gap:var(--space-1);padding:var(--space-2) var(--space-4);border-bottom:var(--line) solid var(--divider);flex:none}
/* A step button shows its number and its name ("2 Window"), so the step is seen and announced (D31) */
.ws-rail__btn{display:inline-flex;align-items:center;gap:var(--icon-gap);font:inherit;white-space:nowrap;height:var(--control-h);padding:0 var(--space-2);color:var(--ink-muted);background:transparent;border:var(--line) solid transparent;border-radius:var(--radius-sm);cursor:pointer}
.ws-rail__num{font-weight:var(--weight-bold)}
.ws-rail__btn[data-done=yes]{color:var(--ink);border-color:var(--border-strong)}
.ws-controls{flex:1;min-width:0;padding:var(--space-4)}
.ws-controls > :first-child{margin-top:0}
.ws-controls h2{font:var(--text-step);margin:0 0 var(--space-3)}
.ws-nav{display:flex;flex-wrap:wrap;justify-content:space-between;gap:var(--space-3);margin-top:var(--space-4)}
/* Side by side: a wide screen, and a landscape-ratio screen at least breakpoint-side-min wide (a landscape
  phone); a narrower landscape screen stacks, as a short screen does, and pins nothing (D41). Either way
  only from H tall, the least height at which the header, the plan bar and the status bar, one row each,
  leave the 3D view 40 % of the screen: build-tokens.py derives H from the bars' tokens, and fails if this
  query says another (D75); scripts/sweep-side-by-side.py measures every short screen */
@media (min-width:56rem) and (min-height:16.5rem),(min-aspect-ratio:4/3) and (min-width:48rem) and (min-height:16.5rem){
  .ws-workbench{grid-template-columns:minmax(0,1fr) min(var(--panel-width),50%);grid-template-rows:var(--header-h) calc(100svh - var(--header-h))}
  .ws-stage{display:flex;flex-direction:column;min-width:0;background:var(--surface)}
  .ws-stage__view{position:relative;flex:1}
  /* The plan bar keeps the phone's one row (D75), and so does the status bar: its parts stay whole but
    the last, the hint, which is cut short with an ellipsis rather than wrap; the whole hint stays in the
    page, for a screen reader */
  .ws-plan{position:static}
  .ws-status{flex-wrap:nowrap}
  .ws-status > *{flex:none;white-space:nowrap}
  .ws-status > :last-child{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}
  .ws-side{height:calc(100svh - var(--header-h))}
  :root:has(.ws-workbench){scroll-padding-top:0}
}
/* Choice: options that look like buttons; the chosen one is tinted, ringed and bold, its radio dotted (D26) */
.ws-choice{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:var(--space-2) 0 0}
/* A second group's caption under a run of Choice buttons, such as "Connectors" under the boards */
.ws-choice + .ws-caps{display:block;margin-top:var(--space-3)}
.ws-choice__opt{position:relative;display:inline-flex}
.ws-choice__input{position:absolute;opacity:0;inset:0;width:100%;height:100%;margin:0;cursor:pointer}
.ws-choice__face{box-sizing:border-box;display:inline-flex;align-items:center;min-height:var(--control-h);padding:0 var(--space-4);color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border-strong);border-radius:var(--radius-sm)}
.ws-choice__face::before,.ws-piece[aria-pressed=true]::before{content:"";flex:none;box-sizing:border-box;width:var(--radio-size);height:var(--radio-size);margin:0 calc(var(--icon-gap) + var(--ring)) 0 var(--ring);border:var(--ring) solid var(--border-strong);border-radius:50%;background:transparent}
.ws-choice__input:hover:not(:disabled) + .ws-choice__face::before{border-color:var(--action)}
/* The chosen radio: an action dot in an action ring; in forced colours, where the inset shadow goes, a filled circle */
.ws-choice__input:checked + .ws-choice__face::before,.ws-piece[aria-pressed=true]::before{border:var(--ring) solid var(--action);background:var(--action);box-shadow:inset 0 0 0 var(--ring) var(--action-tint)}
.ws-choice__input:focus-visible + .ws-choice__face{outline:var(--ring) solid var(--action);outline-offset:var(--ring)}
.ws-choice__input:disabled + .ws-choice__face{opacity:var(--opacity-disabled)}
/* Fira Code: ligatures on for text (so => and -> join), off for numbers and code; zero is slashed for numbers */
.ws-text,.ws-workbench{font-variant-ligatures:contextual}
.ws-input,.ws-status,.ws-plan__line,code,.ws-num{font-variant-ligatures:none;font-feature-settings:"calt" 0,"zero" 1}
/* The browser sets these in its own monospace; keep the one family */
code,kbd,samp,pre{font-family:var(--font-ui)}
/* Nerd Font icons: decoration beside a text label, always aria-hidden. Raised onto the label's capitals:
  the font centres its icons 88.5 units under them (D88). top, not translate: Chromium moves the icon a
  whole pixel for top and half a pixel for translate, which leaves it 1.25px low (measured) */
.ws-ic{display:inline-block;margin-right:var(--icon-gap);font-style:normal;line-height:1;position:relative;top:calc(-1 * var(--icon-rise))}

.ws-sides{display:grid;grid-template-columns:auto var(--sides-w) auto;grid-template-rows:auto var(--sides-h) auto;gap:var(--space-2);align-items:center;justify-items:center;width:max-content;margin-top:var(--space-2)}
.ws-sides__board{grid-area:2/2;width:100%;height:100%;background:var(--surface-sunken);border:var(--line) dashed var(--border-strong);border-radius:var(--radius-sm)}
.ws-sides__top{grid-area:1/2}.ws-sides__left{grid-area:2/1}.ws-sides__right{grid-area:2/3}.ws-sides__bottom{grid-area:3/2}
.ws-sides__btn{display:inline-grid;grid-template-columns:auto auto;column-gap:calc(var(--icon-gap) + var(--ring));align-items:center;justify-content:start;min-height:var(--control-h);padding:0 var(--space-3);font:inherit;color:var(--ink);background:var(--surface-raised);border:var(--line) solid var(--border-strong);border-radius:var(--radius-sm);cursor:pointer;white-space:nowrap}
/* A box drawn pressed or not, the size of the radio, so pressing never moves the layout. The gap to the
  label is the button's own, so icon-gap (in em) is measured at the label's size, as beside a radio */
.ws-sides__btn::before{content:"" / "";grid-area:1/1;box-sizing:border-box;width:var(--radio-size);height:var(--radio-size);margin-left:var(--ring);border:var(--ring) solid var(--border-strong);border-radius:var(--ring)}
.ws-sides__btn[aria-pressed="true"]::before{border-color:var(--action);background:var(--action)}
/* The tick, laid over the box in the same cell with the same margin, so centred on it. It takes the
  small text size: every tick in the font draws about as wide as its em, so at the label's size it
  would fill the box edge to edge. Decoration: the empty text after "/" hides it, like aria-hidden */
.ws-sides__btn[aria-pressed="true"]::after{content:"\eab2";content:"\eab2" / "";grid-area:1/1;place-self:center;margin-left:var(--ring);font:var(--text-label-caps);line-height:1;color:var(--on-action)}
.ws-sides__btn:focus-visible{outline:var(--ring) solid var(--action);outline-offset:var(--ring)}
.ws-warning{color:var(--warning)}

/* The status bar's hint: the mouse's by default, the touch screen's on one */
.ws-hint-touch{display:none}
/* Touch screens: the hint is for fingers. Controls grow to the minimum touch target through control-h's
  touch value, which tokens.css sets */
@media (pointer:coarse){.ws-hint-fine{display:none}.ws-hint-touch{display:inline}}
/* Forced colours (Windows High Contrast and the like) keep only the user's system colours: the chosen
  radio's action dot would turn to Canvas and look unchosen, and a step not reached would take a border
  like a done one. In system colours, each state keeps its own look (check-previews.py renders it) */
@media (forced-colors:active){
  .ws-choice__input:checked + .ws-choice__face::before,.ws-piece[aria-pressed=true]::before{background:CanvasText}
  .ws-rail__btn:not([data-done=yes]):not([aria-current=step]){border-color:Canvas}
}
