@layer overscan {
/* A mechanical switch and the talkback flag above it. See ov-switch.js.
 *
 * 🔴 THE FLAG AND THE HANDLE NEVER SHARE A COLOUR. The flag's word is drawn in
 * the accent, the colour of a confirmed state; the handle and the set
 * position's label are drawn in ink, because they are what the operator did
 * and nothing more. The one light a switch has, the rocker's lamp, is lit
 * from the talkback (data-ov-lit), never from the rocker.
 *
 * Drawn flat, as the part is seen from the front of a panel. A rod is narrow
 * at the bushing and wide at its tip, because the tip is nearer the reader,
 * and it stands end-on at a centre position. Barberpole is static stripes,
 * never animated, so it cannot be read as the mechanism moving.
 */

ov-switch {
  --ov-sw-row: 30px;
  /* How far toward its position's row a rod reaches: short enough to read as
     a bat on a bushing rather than a pointer across the plate. */
  --ov-sw-reach: .75;
  display: inline-grid; gap: 6px; justify-items: start; align-content: start;
  min-inline-size: 0;
  font-family: var(--ov-font-mono); font-size: var(--ov-size-1, 10px); color: var(--ov-ink);
}
ov-switch[data-ov-kind="lever"] { --ov-sw-row: 34px; }
ov-switch[data-ov-kind="rocker"] { --ov-sw-row: 24px; }

/* ---- the talkback flag ------------------------------------------------ */

.ov-switch__flag {
  box-sizing: border-box; min-inline-size: 44px; block-size: 22px; padding-inline: 6px;
  display: grid; place-items: center;
  border: var(--ov-bezel, 1px) solid var(--ov-line-strong); border-radius: 2px;
  background: var(--ov-field); color: var(--ov-accent);
  font-size: var(--ov-size-2, 12px); letter-spacing: .12em;
}
.ov-switch__flag[data-ov-flag="barberpole"] {
  background: repeating-linear-gradient(-45deg, var(--ov-ink) 0 4px, var(--ov-field) 4px 8px);
}

/* ---- the plate: positions, and the mechanism beside them ------------- */

.ov-switch__plate {
  position: relative; display: grid; grid-auto-rows: var(--ov-sw-row);
  min-inline-size: 13ch;
}
.ov-switch__pos {
  font: inherit; letter-spacing: .12em; text-transform: var(--ov-case);
  background: none; border: 0; margin: 0; cursor: pointer;
  color: var(--ov-faint); text-align: start;
  padding: 0 8px 0 58px;
}
.ov-switch__pos:hover:not(:disabled) { color: var(--ov-ink); }
.ov-switch__pos:focus-visible { outline: var(--ov-rule, 1px) solid var(--ov-accent); outline-offset: -1px; }
.ov-switch__pos:disabled { cursor: not-allowed; }
.ov-switch__pos[data-ov-set] { color: var(--ov-ink); }

.ov-switch__mech { position: absolute; inset-block: 0; inset-inline-start: 0; inline-size: 44px; pointer-events: none; z-index: 1; }
.ov-switch__mech > i { position: absolute; inset-inline-start: 50%; }

/* The bushing: one flat ring and its hole. */
.ov-switch__pivot {
  inset-block-start: 50%; inline-size: 22px; block-size: 22px; translate: -50% -50%;
  border-radius: 50%; background: var(--ov-panel); box-shadow: inset 0 0 0 1px var(--ov-line-strong);
}
.ov-switch__pivot::after {
  content: ""; position: absolute; inset: 7px; border-radius: 50%; background: var(--ov-field);
}

/* The rod. Its reach is scaled from the bushing, so its tip stops short of
   the position's row. */
.ov-switch__bat {
  inline-size: 9px; translate: -50% 0; inset-block-start: 50%; block-size: 0;
  background: color-mix(in srgb, var(--ov-ink) 62%, var(--ov-field));
}
ov-switch[data-ov-lean="up"] .ov-switch__bat {
  inset-block-start: calc(50% - (50% - var(--ov-sw-y)) * var(--ov-sw-reach));
  block-size: calc((50% - var(--ov-sw-y)) * var(--ov-sw-reach));
  clip-path: polygon(0 0, 100% 0, 68% 100%, 32% 100%);
}
ov-switch[data-ov-lean="down"] .ov-switch__bat {
  inset-block-start: 50%;
  block-size: calc((var(--ov-sw-y) - 50%) * var(--ov-sw-reach));
  clip-path: polygon(32% 0, 68% 0, 100% 100%, 0 100%);
}
/* The round end, foreshortened: an ellipse while it leans, a circle end-on. */
.ov-switch__tip {
  inset-block-start: calc(50% + (var(--ov-sw-y, 50%) - 50%) * var(--ov-sw-reach));
  inline-size: 13px; block-size: 9px; translate: -50% -50%;
  border-radius: 50%; background: var(--ov-ink);
}
ov-switch[data-ov-lean="mid"] .ov-switch__tip { block-size: 13px; }
/* Nothing set: no handle is drawn, rather than one invented at the centre. */
ov-switch[data-ov-handle=""] :is(.ov-switch__bat, .ov-switch__tip) { display: none; }

/* look="triangle": a flat blade that widens to its end, seen edge-on there. */
ov-switch[data-ov-look="triangle"] .ov-switch__bat { inline-size: 20px; }
ov-switch[data-ov-look="triangle"][data-ov-lean="up"] .ov-switch__bat { clip-path: polygon(0 0, 100% 0, 58% 100%, 42% 100%); }
ov-switch[data-ov-look="triangle"][data-ov-lean="down"] .ov-switch__bat { clip-path: polygon(42% 0, 58% 0, 100% 100%, 0 100%); }
ov-switch[data-ov-look="triangle"] .ov-switch__tip { inline-size: 20px; block-size: 4px; border-radius: 0; }
ov-switch[data-ov-look="triangle"][data-ov-lean="mid"] .ov-switch__tip { block-size: 7px; }

/* Flick: a dashed ring round the bushing, because the rod springs back to it. */
ov-switch[data-ov-kind="flick"] .ov-switch__pivot { outline: var(--ov-rule, 1px) dashed var(--ov-line-strong); outline-offset: 2px; }
.ov-switch__cmd { color: var(--ov-faint); letter-spacing: .1em; }
.ov-switch__cmd:empty { display: none; }

/* ---- lever: a slot with a detent per position, and a bar in it ------- */

ov-switch[data-ov-kind="lever"] :is(.ov-switch__pivot, .ov-switch__bat) { display: none; }
ov-switch[data-ov-kind="lever"] .ov-switch__mech::before {
  content: ""; position: absolute; inset-block: 4px; inset-inline-start: 50%; inline-size: 4px; translate: -50% 0;
  background: var(--ov-field); box-shadow: 0 0 0 1px var(--ov-line-strong);
}
ov-switch[data-ov-kind="lever"] .ov-switch__pos { position: relative; }
ov-switch[data-ov-kind="lever"] .ov-switch__pos::before {
  content: ""; position: absolute; inset-inline-start: 11px; inline-size: 20px; inset-block-start: 50%;
  border-block-start: var(--ov-rule, 1px) solid var(--ov-line-strong);
}
ov-switch[data-ov-kind="lever"] .ov-switch__tip {
  inset-block-start: var(--ov-sw-y, 50%);
  inline-size: 28px; block-size: 10px; border-radius: 1px;
  background: var(--ov-ink);
  box-shadow: inset 0 -3px 0 color-mix(in srgb, var(--ov-ink) 55%, var(--ov-field));
}

/* ---- rocker: two halves, the positions printed on them --------------- */
/* The pressed half sits low; the raised half shows its edge, the side that
 * tilts toward the reader. */

ov-switch[data-ov-kind="rocker"] .ov-switch__mech { display: none; }
ov-switch[data-ov-kind="rocker"] .ov-switch__plate {
  min-inline-size: 0; inline-size: 36px; padding: 3px; border-radius: 3px;
  background: var(--ov-field); box-shadow: inset 0 0 0 1px var(--ov-line-strong);
}
ov-switch[data-ov-kind="rocker"] .ov-switch__pos {
  padding: 0; text-align: center; letter-spacing: 0; font-size: var(--ov-size-2, 12px);
  color: var(--ov-faint); background: color-mix(in srgb, var(--ov-ink) 20%, var(--ov-panel));
}
ov-switch[data-ov-kind="rocker"] .ov-switch__pos:first-of-type { border-radius: 2px 2px 0 0; }
ov-switch[data-ov-kind="rocker"] .ov-switch__pos:last-of-type { border-radius: 0 0 2px 2px; }
ov-switch[data-ov-kind="rocker"] .ov-switch__pos:first-of-type:not([data-ov-set]) { box-shadow: inset 0 3px 0 color-mix(in srgb, var(--ov-ink) 40%, var(--ov-panel)); }
ov-switch[data-ov-kind="rocker"] .ov-switch__pos:last-of-type:not([data-ov-set]) { box-shadow: inset 0 -3px 0 color-mix(in srgb, var(--ov-ink) 40%, var(--ov-panel)); }
ov-switch[data-ov-kind="rocker"] .ov-switch__pos[data-ov-set] {
  color: var(--ov-ink); background: color-mix(in srgb, var(--ov-ink) 6%, var(--ov-field));
}
/* ⭐ Lit only while the talkback confirms the lamp's position. */
ov-switch[data-ov-kind="rocker"][data-ov-lit] .ov-switch__pos {
  color: var(--ov-field); background: color-mix(in srgb, var(--ov-accent) 75%, var(--ov-field));
}
ov-switch[data-ov-kind="rocker"][data-ov-lit] .ov-switch__pos[data-ov-set] {
  background: color-mix(in srgb, var(--ov-accent) 50%, var(--ov-field));
}

/* ---- the guard: a translucent cover on a hinge ------------------------ */
/* The switch shows through it and the positions stay readable beside it; they
 * are still disabled until the switch is armed. Room is kept above the plate
 * whether the cover is shut or open, so arming does not move the layout. */

ov-switch[guard] .ov-switch__plate { margin-block-start: 42px; }
.ov-switch__guard {
  font: inherit; border: 0; margin: 0; cursor: pointer;
  position: absolute; z-index: 2;
  inset: -9px auto -5px -6px; inline-size: 56px;
  border-radius: 8px 8px 3px 3px;
  background: color-mix(in srgb, var(--ov-alarm) 30%, transparent);
  box-shadow: inset 0 0 0 1px var(--ov-alarm);
  display: grid; place-items: end center; padding: 0 0 3px;
  transform-origin: 50% 0;
  transition: transform var(--ov-dur-fast, .15s) var(--ov-ease, ease), background var(--ov-dur-fast, .15s);
}
ov-switch[data-ov-kind="rocker"] .ov-switch__guard { inline-size: 48px; }
.ov-switch__guardword { color: var(--ov-alarm); font-size: 8px; letter-spacing: .08em; background: var(--ov-field); padding: 0 2px; }
/* The hinge belongs to the panel, so it stays put when the cover moves. */
ov-switch[guard] .ov-switch__plate::before {
  content: ""; position: absolute; z-index: 3;
  inset-inline-start: -2px; inline-size: 48px; inset-block-start: -13px; block-size: 4px;
  background: var(--ov-line-strong);
}
/* Open: lifted up and back past the hinge. A guard that is open is still
   there, and the switch below it is plainly uncovered. */
.ov-switch__guard:hover { background: color-mix(in srgb, var(--ov-alarm) 42%, transparent); }
.ov-switch__guard:focus-visible { outline: var(--ov-rule, 1px) solid var(--ov-accent); outline-offset: 2px; }
/* Clicking the open cover closes it again, which is the safe control. */
ov-switch[data-ov-armed] .ov-switch__guard {
  transform: translateY(-4px) scaleY(-.45);
  background: color-mix(in srgb, var(--ov-alarm) 16%, transparent);
}
ov-switch[data-ov-armed] .ov-switch__guardword { visibility: hidden; }

/* ---- under the plate: sized once, whatever the state ------------------ */
/* 🔴 NOTHING UNDER THE PLATE CHANGES SIZE WITH THE STATE. Switches stand in
 * rows, so a reading that appears or a last command written out must not move
 * the neighbours or the row. The reading keeps its one line when there is
 * nothing to say, and a flick switch keeps the line its command is written on. */
ov-switch[data-ov-kind="flick"] .ov-switch__cmd { display: block; min-block-size: 1lh; white-space: nowrap; inline-size: 0; min-inline-size: 100%; }

/* The reading never wraps, and its width is set once: a hidden copy of the
   longest word it can say (and on a flick switch the longest last command)
   shares the word's cell, so a narrow rocker is as wide as NOT IN POSITION.
   No word runs onto the next switch and no state resizes this one. The
   explanation takes the width rather than setting it. */
.ov-switch__out { justify-self: stretch; }
.ov-switch__word { grid-area: 1 / 1; min-block-size: 1lh; white-space: nowrap; }
.ov-switch__room { visibility: hidden; pointer-events: none; }
.ov-switch__room--cmd { grid-area: 1 / 1; white-space: nowrap; letter-spacing: .1em; }
/* With chrome="full" the bar's rule is 3px, or 6px when refusing; the padding
   gives back the difference, so the tone never changes the switch's width. */
ov-switch:where([data-ov-chrome="full"], [data-ov-chrome="full"] *) .ov-switch__out.ov-switch__out.ov-switch__out { padding-inline-start: 13px; }
ov-switch:where([data-ov-chrome="full"], [data-ov-chrome="full"] *) .ov-switch__out.ov-switch__out.ov-switch__out[data-ov-tone="bad"] { padding-inline-start: 10px; }
/* chrome.css takes an ok box out of the layout, which is what moved a row of
   switches. Here it keeps its line and paints nothing. Four classes outrank
   both of that file's ok rules, (0,4,0) and (0,5,0). */
ov-switch .ov-switch__out.ov-switch__out.ov-switch__out.ov-switch__out[data-ov-tone="ok"] { display: grid; visibility: hidden; }
/* The explanation, shown only with chrome="full", is one line that keeps its
   place when there is nothing to explain; a long one ends in an ellipsis and
   carries the whole sentence as its title. It is prose, never the refusal. */
.ov-switch__why { grid-row: 2; inline-size: 0; min-inline-size: 100%; color: var(--ov-ink); letter-spacing: .06em; line-height: 1.45; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ov-switch__why:empty { display: none; }
ov-switch:where([data-ov-chrome="full"], [data-ov-chrome="full"] *) .ov-switch__why:empty { display: block; min-block-size: 1lh; }

@media (prefers-reduced-motion: reduce) {
  .ov-switch__guard { transition: none; }
}
}
