/* Shared layout + site theme tokens.
   Site default = light / purple (same tokens as event scheme `light-purple`).
   Public event/series pages override via applyColorScheme() on <html>. */
:root {
  /* One measure for every page. Widths were set page by page and ranged from
     400px on a ticket to 1180px on an event, so no two screens lined up with
     each other. Change it here, not in twenty stylesheets. */
  --ot-measure: 1000px;
  --ot-measure-pad: 32px;
  --ot-gutter: 20px;
  --ot-gutter-sm: 16px;
  /* The site header's height. Anything else that sticks to the top has to
     clear it, and three places need the same number, so it lives here rather
     than being typed out three times and drifting. */
  --ot-header-h: 60px;

  /* Surface - white base */
  --ot-bg: #ffffff;
  --ot-surface: #ffffff;
  --ot-surface-2: #f4f4f5;
  --ot-text: #111827;
  --ot-muted: #374151;
  --ot-faint: #4b5563;
  --ot-border: #e5e7eb;
  --ot-border-strong: #d1d5db;
  /* Control boundary, held apart from the decorative borders above: a field edge
     is the only thing identifying the input, so it needs 3:1 (WCAG 1.4.11). */
  --ot-field-border: #6b7280;
  --ot-input-bg: #ffffff;
  --ot-chip: rgba(124, 58, 237, 0.10);
  --ot-success: #047857;
  /* Success as a *surface*, held apart from --ot-success above, which is a
     text colour on a light ground and correct as it is.
     #047857 behind the #06281c label these buttons use is 2.88:1 - under even
     the 3:1 floor for large text, and the reason the hover state was the only
     legible one. This is 9.45:1, and the hover below is 8.10:1, so the
     affordance does not cost the contrast. */
  --ot-success-surface: #4be0ab;
  --ot-success-surface-hover: #38d19b;
  --ot-success-ink: #06281c;
  --ot-danger: #b91c1c;
  --ot-danger-bg: #fef2f2;
  --ot-color-scheme: light;
  --ot-skel-mid: #e5e7eb;
  --ot-shadow: 0 8px 28px rgba(15, 23, 42, 0.08);

  /* Elevation, as a scale rather than a value per component.
     Each level is layered: a hairline ring that draws the edge, a tight
     contact shadow, and a broad ambient one. A single blur reads as a sticker;
     two read as an object above a surface.

     --ot-shadow above is left exactly as it was. Every page that uses it keeps
     the look it has - these are additive, and only what opts in moves. */
  --ot-elev-1:
    0 0 0 1px rgba(16, 24, 40, 0.05),
    0 1px 3px rgba(16, 24, 40, 0.07);
  --ot-elev-2:
    0 0 0 1px rgba(16, 24, 40, 0.05),
    0 2px 6px rgba(16, 24, 40, 0.06),
    0 12px 24px -8px rgba(16, 24, 40, 0.18);
  --ot-elev-3:
    0 0 0 1px rgba(16, 24, 40, 0.04),
    0 2px 6px rgba(16, 24, 40, 0.06),
    0 22px 44px -12px rgba(16, 24, 40, 0.28);

  /* Radius, tied to size: the bigger the surface, the rounder its corner. A
     9px button inside a 16px card is the kind of mismatch that reads as an
     accident, because it is one. */
  --ot-r-sm: 8px;    /* controls under 40px: chips, small buttons */
  --ot-r-md: 12px;   /* fields, buttons, list rows */
  --ot-r-lg: 16px;   /* cards, popovers */
  --ot-r-xl: 20px;   /* modal sheets */

  /* One duration and one easing, so a popover and a sheet feel like the same
     product opening two different things. */
  --ot-t-pop: 120ms;
  --ot-t-sheet: 240ms;
  --ot-ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* The scrim. Lighter than a blackout, and blurred, so the page behind is
     pushed back rather than hidden - you can still see what you were working
     on, which is half of what tells you the sheet is temporary. */
  --ot-scrim: rgba(9, 14, 24, 0.38);
  --ot-icon-well: #f3f4f6;

  /* Accent - light purple */
  --ot-link: #5b21b6;
  --ot-link-hover: #4c1d95;
  --ot-cta: #ddd6fe;
  --ot-cta-hover: #c4b5fd;
  --ot-cta-ink: #1e1b4b;
  --ot-accent: #7c3aed;
  --ot-accent-soft: rgba(124, 58, 237, 0.10);
  --ot-accent-veil: rgba(124, 58, 237, 0.58);
  --ot-header: linear-gradient(180deg, #f5f3ff 0%, #ffffff 72%);
  --ot-focus-ring: #7c3aed;

  /* Cards / subscribe tiles */
  --ot-card-0: #ffffff;
  --ot-card-1: #fafafa;
  --ot-card-2: #f8fafc;
  --ot-card-3: #f9fafb;
  --ot-card-4: #f5f5f4;
  --ot-card-ink: #111827;
  --ot-card-muted: #374151;
  --ot-card-border: #e5e7eb;
  --ot-subscribe-card: linear-gradient(145deg, #ffffff 0%, #faf5ff 48%, #f5f3ff 100%);

  /* Legacy aliases */
  --ot-pop: var(--ot-cta);
  --ot-pop-hover: var(--ot-cta-hover);
  --ot-pop-ink: var(--ot-cta-ink);
}

/* Make all form controls use a white fill for consistency across the app.
   Uses !important to override inline styles that previously set var(--ot-surface).
*/
/* :not(.ne-toggle) - a switch is an input with appearance:none whose whole
   look is its own background and border, and this rule sets both with
   !important, so it painted the track white with a hairline round it and left
   the knob invisible on top. Nothing else here needs excluding: the arms below
   name text-like types, and a switch is a checkbox. */
input:not(.ne-toggle):not(.ot-toggle), textarea, select, .es-input, .es-modal input, .tk-modal input, .pass-actions input, input[type="text"], input[type="email"], input[type="password"], input[type="search"], input[type="number"], input[type="date"], input[type="time"], input[type="datetime-local"], input[type="month"], input[type="week"] {
  background: var(--ot-input-bg, #ffffff) !important;
  color: var(--ot-text, #111827) !important;
  border: 1px solid var(--ot-field-border, #6b7280) !important;
}
input::placeholder, textarea::placeholder {
  color: var(--ot-faint, #4b5563) !important;
}

/* Invalid fields. The rule above sets every input border with !important, which
   silently defeated the inline red "bad" border each page was already computing
  , so an invalid field looked identical to a valid one. Driving the error state
   off aria-invalid fixes it in one place and keeps the marker and the assistive
   -tech signal in step. The ring is deliberate: border colour alone is a single
   cue, and a shape change carries for anyone who cannot separate red from grey. */
/* Scoped by [data-ot-surface] as well as bare, because some pages carry their
   own `[data-ot-surface="light"] input { border-color: ... !important }` in an
   inline <style>. That has the same specificity as a bare input[aria-invalid]
   rule but loads later, so it would win the tie and keep invalid fields grey. */
[data-ot-surface] input[aria-invalid="true"],
[data-ot-surface] textarea[aria-invalid="true"],
[data-ot-surface] select[aria-invalid="true"],
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] {
  border-color: var(--ot-danger, #b91c1c) !important;
  box-shadow: 0 0 0 3px var(--ot-danger-bg, #fef2f2) !important;
}

/* Keyboard focus. Every interactive element used to fall back to the browser's
   default ring, and inputs suppressed it outright, so there was no consistent
   focus state anywhere. One accent ring, offset so it reads on both light and
   dark surfaces. :focus-visible keeps it off mouse clicks. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid var(--ot-focus-ring, #7c3aed) !important;
  outline-offset: 2px !important;
  border-radius: 4px;
}

/* Skip link: off-screen until focused, then pinned over the header. */
.ot-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
}
.ot-skip-link:focus {
  left: 12px;
  top: 12px;
  background: var(--ot-surface, #ffffff);
  color: var(--ot-text, #111827);
  border: 1px solid var(--ot-border-strong, #d1d5db);
  border-radius: 8px;
  padding: 10px 16px;
  font-weight: 700;
  text-decoration: none;
}

html {
  color-scheme: var(--ot-color-scheme, light);
  background: var(--ot-bg, #ffffff);
}

body {
  background: var(--ot-bg, #ffffff);
  color: var(--ot-text, #111827);
}

@media (max-width: 860px) {
  :root {
    --ot-gutter: 16px;
    --ot-gutter-sm: 16px;
  }
}

@media (max-width: 480px) {
  :root {
    --ot-gutter: 16px;
  }
}

/* Native date/time controls have large intrinsic min-widths on iOS/WebKit and
   will overhang padded columns unless width is forced to the container. */
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"],
input[type="week"] {
  display: block;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  filter: none;
}
[data-ot-surface="light"] {
  color-scheme: light;
}
[data-ot-surface="dark"] {
  color-scheme: dark;

  /* Elevation has to be re-stated on a dark ground, not reused. A black
     shadow over near-black is invisible, so the ambient layers go darker and
     wider; and the hairline ring inverts, because on dark an edge reads as a
     lighter line rather than a darker one. */
  --ot-elev-1:
    0 0 0 1px rgba(255, 255, 255, 0.06),
    0 1px 3px rgba(0, 0, 0, 0.45);
  --ot-elev-2:
    0 0 0 1px rgba(255, 255, 255, 0.07),
    0 2px 6px rgba(0, 0, 0, 0.45),
    0 12px 24px -8px rgba(0, 0, 0, 0.6);
  --ot-elev-3:
    0 0 0 1px rgba(255, 255, 255, 0.08),
    0 2px 6px rgba(0, 0, 0, 0.5),
    0 22px 44px -12px rgba(0, 0, 0, 0.7);
  --ot-scrim: rgba(2, 5, 12, 0.6);
}
[data-ot-surface="dark"] input[type="date"]::-webkit-calendar-picker-indicator,
[data-ot-surface="dark"] input[type="time"]::-webkit-calendar-picker-indicator,
[data-ot-surface="dark"] input[type="datetime-local"]::-webkit-calendar-picker-indicator {
  filter: invert(0.7);
}

/* The outer wrapper a signed-in page draws around itself.
   On its own it owns the viewport and wants the full height, so a short page
   still pushes its footer to the bottom. Inside the app shell it owns nothing:
   the shell holds the viewport, the rail sits beside it and the bar sits under
   it, and a full-height child in that row is a page taller than the window it
   is in. Embedded, it fills the space it is given instead.

   The flex column was an inline style repeated on each page before this. */
.ot-page-shell {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  /* The Gradient surface's wash, on the shared shell too: without this the
     four pages using this class stay flat while the public ones carry it.
     --ot-bg stays a colour, because it is read as one elsewhere. */
  background-color: var(--ot-bg, #ffffff);
  background-image: var(--ot-bg-image, none);
  background-repeat: no-repeat;
  color: var(--ot-text, #111827);
}
.ot-page-shell.is-embedded {
  min-height: 0;
  /* Basis auto, so the page's own height is what the shell's scroller
     measures. With a basis of 0 a long page reports no height and the frame
     has nothing to scroll. */
  flex: 1 0 auto;
}

@media (max-width: 720px) {
  html, body {
    overflow-x: clip;
  }
  img, video, canvas, svg {
    max-width: 100%;
  }
}

/* Stop iOS zooming the page when a field is focused.
 *
 * Safari on iPhone zooms in whenever a focused input's text is under 16px, and
 * does not zoom back out, so the page is left scrolled sideways and the next
 * field is off screen. Every form in the app was doing it: the event editor
 * alone has forty-five fields at 13 to 14px, so creating an event on a phone
 * meant the page lurching on every single tap.
 *
 * Only on small screens, so the denser type on desktop is untouched. It needs
 * !important because most of these sizes are inline styles inside the x-dc
 * templates, which a stylesheet cannot otherwise outrank; the alternative is
 * editing several hundred individual declarations and hoping none are missed.
 *
 * Checkboxes, radios and the like are excluded - font-size does not set their
 * text and can alter their box in some browsers.
 */
@media (max-width: 768px) {
  :root{ --ot-header-h: 56px; }

  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="hidden"]),
  select,
  textarea {
    font-size: 16px !important;
  }
}

/* ── The day-grouped timeline ──────────────────────────────────────────────
 * Shared by the organiser page and the series page. It lived in organiser.html
 * and the series page wanted the same thing; two copies of it would have been
 * two copies to keep in step, and the whole point is that they look alike.
 *
 * Dates are headings said once, with the rows under them carrying only their
 * time - the grid this replaced repeated the full date on every card, so a
 * fortnightly series was twelve dates differing by one number.
 */
  .ot-tl-note{display:flex;align-items:center;gap:7px;font-size:12.5px;color:var(--ot-faint);font-weight:500;margin:0 0 14px;}
  .ot-tl{position:relative;padding-left:20px;}
  .ot-tl::before{content:'';position:absolute;left:4px;top:10px;bottom:10px;width:1px;background:var(--ot-border);}
  .ot-tl-day{position:relative;display:flex;align-items:baseline;gap:8px;margin:26px 0 12px;}
  .ot-tl-day:first-child{margin-top:0;}
  .ot-tl-day-dot{position:absolute;left:-20px;top:5px;width:9px;height:9px;border-radius:999px;background:var(--ot-border-strong);}
  .ot-tl-day-dot.is-next{background:var(--ot-accent);}
  .ot-tl-day-t{font-size:15px;font-weight:800;color:var(--ot-text);}
  .ot-tl-day-s{font-size:12.5px;color:var(--ot-faint);font-weight:500;}
  /* A card each, not rows divided by a hairline. Flush rows with a 1px rule
     between them read as a table; the events are separate things, and at a
     glance the card is what says so. Kept quiet: one border, no shadow until
     the row is hovered, and only where the row is a link. */
  /* Lighter than the page it sits on, and still the colour the page is.
     On a light scheme the ground and the panels are the same tint on purpose,
     with the borders doing the separating - which is right for a table or a
     form, both of which are part of the page. A card in a list of cards is
     not: it wants to sit above the page rather than beside it, and at one
     tint the only thing saying so was a 1px border.

     Mixed toward white rather than set to it, so what comes out is a paler
     wash of whatever the organiser chose - teal stays teal - instead of a
     white slab on a coloured page. The flat background in front of it is what
     a browser without color-mix keeps. */
  .ot-tl-row{display:grid;grid-template-columns:minmax(0,1fr) 72px;gap:16px;align-items:center;
    padding:16px 18px;margin:0 0 10px;background:var(--ot-surface);
    background:color-mix(in srgb, #ffffff 55%, var(--ot-surface));
    border:1px solid var(--ot-border);
    /* The edge does the separating on the light grounds, because the card
       cannot do it: --ot-bg is already near white there, so the 55% white mix
       above lands at 1.03:1 against it and has nowhere further to go. The
       border was 1.14:1 on top of that - a card you had to look for. Darkened
       to 1.57:1, which is the figure the dark ground was taken to, so both
       read the same. --ot-border itself is left alone: it rules tables and
       fields, where a hairline is the right answer.
       The flat declaration in front is what a browser without color-mix keeps,
       and the dark override below replaces both. */
    border-color:var(--ot-border-strong);
    border-color:color-mix(in srgb, #000000 14%, var(--ot-border));
    border-radius:16px;
    text-decoration:none;color:var(--ot-text);
    transition:border-color .15s ease, box-shadow .15s ease;}
  /* White has nowhere lighter to go, so it keeps the flat card above.

     Dark was supposed to lift its panels off its ground and did not. Measured:
     --ot-surface against --ot-bg is 1.04:1 and --ot-border against that card is
     1.27:1, so the page read as one flat sheet with the cards implied by their
     contents and nothing else. Every dark scheme shares those two neutrals, so
     it was every dark series page and every dark ticket list.

     The card is lifted to 1.31:1 off the ground with an edge at 1.56:1, and
     hover brighter again at 2.48:1 so it still has somewhere to go - it did not
     before, because --ot-border-strong on dark (#2a3a5f) is dimmer against a
     lifted card than the resting border needs to be.

     Mixed rather than named, so it stays derived from whatever ground the
     scheme sets rather than pinned to today's neutrals. The flat declaration
     in front of each is what a browser without color-mix keeps. */
  [data-ot-surface="dark"] .ot-tl-row{
    background:var(--ot-surface-2);
    background:color-mix(in srgb, #ffffff 9%, var(--ot-surface));
    border-color:var(--ot-border-strong);
    border-color:color-mix(in srgb, #ffffff 14%, var(--ot-border));
  }
  .ot-tl-row:last-child{margin-bottom:0;}
  a.ot-tl-row:hover{border-color:var(--ot-border-strong);box-shadow:var(--ot-shadow);}
  [data-ot-surface="dark"] a.ot-tl-row:hover{
    border-color:color-mix(in srgb, #ffffff 28%, var(--ot-border));
  }
  a.ot-tl-row:hover .ot-tl-row-title{color:var(--ot-link-hover);}
  .ot-tl-row-body{display:flex;flex-direction:column;gap:2px;min-width:0;}
  .ot-tl-row-when{font-size:12px;font-weight:700;color:var(--ot-link);}
  .ot-tl-row-title{font-size:16px;font-weight:800;line-height:1.25;overflow-wrap:anywhere;}
  .ot-tl-row-place{font-size:13px;color:var(--ot-muted);overflow-wrap:anywhere;}
  .ot-tl-row-price{display:inline-flex;align-self:flex-start;margin-top:6px;padding:3px 9px;border-radius:999px;
    font-size:12px;font-weight:700;background:var(--ot-accent-soft);color:var(--ot-link);}
  .ot-tl-row-thumb{width:72px;height:72px;flex:0 0 72px;border-radius:12px;object-fit:cover;display:block;background:var(--ot-surface-2);}
  .ot-tl-row-thumb.is-blank{display:grid;place-items:center;font-size:24px;font-weight:800;color:var(--ot-link);border:1px solid var(--ot-border);}
  .ot-tl-hosts{display:flex;align-items:center;gap:7px;margin-top:4px;}
  .ot-tl-host-stack{display:inline-flex;}
  .ot-tl-host{width:18px;height:18px;border-radius:999px;border:1.5px solid var(--ot-surface);background:var(--ot-accent-soft);
    color:var(--ot-link);display:grid;place-items:center;font-size:9px;font-weight:800;overflow:hidden;position:relative;}
  .ot-tl-host + .ot-tl-host{margin-left:-6px;}
  .ot-tl-host img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
  .ot-tl-hosts-by{font-size:12.5px;color:var(--ot-muted);font-weight:500;}

  /* Phones. Smaller thumbnail and tighter type, but the card keeps its padding
     - these numbers were written when a row was a flush line of text with a
     rule under it, and 'padding:11px 0' put the thumbnail against the border. */
  @media (max-width:720px){
    .ot-tl-row{grid-template-columns:minmax(0,1fr) 58px;gap:12px;padding:13px 14px;}
    .ot-tl-row-thumb{width:58px;height:58px;flex:0 0 58px;border-radius:10px;}
    .ot-tl-row-title{font-size:15px;}
  }
/* ── Component placeholders ───────────────────────────────────────────────
 *
 * support.js draws a box while a component loads, and its own styling for it
 * is theme-agnostic: an 8% currentColor fill inside a *50%* currentColor
 * border. On these pages currentColor is near-black body text, so the header
 * announced itself as a full-width 60px rectangle in a hard dark outline,
 * every navigation, before the real header replaced it.
 *
 * The runtime prepends its stylesheet (document.head.prepend) precisely so
 * pages can override it. `html` on the selector is belt and braces in case a
 * future build appends instead.
 */
html .sc-placeholder {
  background: var(--ot-surface-2);
  border-color: transparent;
  border-radius: 10px;
}

/* The header and footer are chrome, not content: their space is already
 * reserved by hint-size, so nothing shifts when they arrive. A skeleton for
 * them only draws attention to a gap that is about to close - the title
 * attribute carries the component name, which is what makes this targetable.
 */
html .sc-placeholder[title="Header"],
html .sc-placeholder[title="Footer"] {
  background: transparent;
  border-color: transparent;
}

/* Except for the header's own bar. The header is a component, so on a fresh
 * document it does not exist until support.js has fetched and mounted it - and
 * a cross-document view transition snapshots the new page before that. The bar
 * therefore vanished on every navigation and came back a moment later, which
 * is the one thing that gives away that a page was loaded.
 *
 * Painting the placeholder as the bar itself keeps it there across the join:
 * same surface, same 60px, same hairline. Only its contents fill in.
 *
 * It also claims the transition name, which the real header gives up while it
 * is absent - the two never exist at the same moment, so the name is never
 * held twice, and every frame of the navigation has exactly one header-shaped
 * thing to match against.
 */
html .sc-placeholder[title="Header"] {
  background: var(--ot-surface-2);
  border-bottom: 1px solid var(--ot-border);
  border-radius: 0;
  view-transition-name: ot-header;
}

/* ── Icon tools ────────────────────────────────────────────────────────────
 * A round icon button that names itself on hover. Used by the dashboard's
 * preview panel and by the Orders toolbar, which is why it lives here rather
 * than in either of them.
 *
 * data-tip rather than title: the browser's own tooltip arrives after a delay
 * long enough that you have already clicked to find out what the button does.
 */
  .ot-tool{
    position:relative;display:inline-flex;align-items:center;justify-content:center;gap:7px;
    min-width:40px;height:40px;padding:0 11px;border-radius:999px;
    background:transparent;border:1px solid transparent;color:var(--ot-muted);
    cursor:pointer;text-decoration:none;font-family:inherit;font-size:13.5px;font-weight:700;
    transition:background .15s ease,color .15s ease;
  }
  .ot-tool svg{width:21px;height:21px;display:block;}
  .ot-tool:focus-visible{background:var(--ot-accent-soft);color:var(--ot-text);}
  .ot-tool.is-on{background:var(--ot-accent-soft);color:var(--ot-text);}
  .ot-tool[disabled]{opacity:.5;cursor:default;}
  /* Hover only where there is something to hover with.
     A touch screen has no hover-out: the tap leaves :hover on the button until
     something else is tapped, and this rule paints the same accent the .is-on
     state does - so a toggle switched off went on looking switched on. The
     tooltip below was already turned off on touch for the same reason; this is
     the other half of it. :focus-visible stays outside, since that is the
     keyboard and it does not linger. */
  @media (hover: hover){
    .ot-tool:hover{background:var(--ot-accent-soft);color:var(--ot-text);}
    .ot-tool[disabled]:hover{background:transparent;color:var(--ot-muted);}
  }
  /* Anchored to the button's own edge, not centred on it: centred, the tip on
     the leftmost tool ran past its container and was clipped by it. */
  /* [data-tip], not bare ::after - a tool without one (the close cross in a
     modal, which is named by aria-label alone) was rendering an empty black
     pill on hover. */
  .ot-tool[data-tip]::after{
    content:attr(data-tip);position:absolute;top:calc(100% + 6px);left:0;
    background:var(--ot-text);color:var(--ot-bg);font-size:11.5px;font-weight:600;line-height:1;
    padding:6px 8px;border-radius:7px;white-space:nowrap;pointer-events:none;
    opacity:0;transition:opacity .12s ease;z-index:3;
  }
  .ot-tool.is-right[data-tip]::after{left:auto;right:0;}
  .ot-tool[data-tip]:hover::after,.ot-tool[data-tip]:focus-visible::after{opacity:1;}
  /* A touch screen has no hover-out. Tapping a tool left its tooltip stuck
     open over the row beneath it until something else was tapped, and the tip
     only ever repeated the aria-label, which the button already carries. */
  @media (hover: none){
    .ot-tool[data-tip]::after{content:none;}
  }


/* A setting is a switch.
 *
 * The builder ships its own copy of this as .ne-toggle, inside its own <style>
 * block, and it stays there: those class names are load-bearing in a file that
 * does not load this stylesheet's rules in the same order, and moving them to
 * save eleven lines is a change with no user on the other end. This is the one
 * every other page uses - the same 44x26 track, the same knob, the same
 * reasoning about contrast, which is written out there.
 *
 * Both are excluded from the form-control rule above, which sets background
 * and border with !important: a switch is an input whose whole appearance is
 * its background and border, so that rule paints the track white with a
 * hairline round it and leaves the knob invisible on top.
 */
.ot-toggle-row{
  display:flex;align-items:center;gap:12px;width:100%;box-sizing:border-box;
  padding:12px 14px;border:1px solid var(--ot-border-strong);border-radius:12px;
  background:var(--ot-surface);cursor:pointer;user-select:none;
}
.ot-toggle-row.is-on{border-color:var(--ot-accent);background:var(--ot-surface-2);}
.ot-toggle-text{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px;}
.ot-toggle-title{font-size:13.5px;font-weight:700;color:var(--ot-text);line-height:1.35;}
.ot-toggle-hint{font-size:12px;font-weight:500;color:var(--ot-muted);line-height:1.4;}
.ot-toggle{
  appearance:none;-webkit-appearance:none;margin:0;flex:0 0 auto;
  width:44px;height:26px;border-radius:999px;cursor:pointer;position:relative;
  border:none;background:var(--ot-muted);transition:background .15s ease;
}
.ot-toggle::after{
  content:'';position:absolute;top:3px;left:3px;width:20px;height:20px;
  border-radius:50%;background:var(--ot-surface);transition:transform .15s ease;
}
.ot-toggle:checked{background:var(--ot-accent);}
.ot-toggle:checked::after{transform:translateX(18px);}
.ot-toggle:focus-visible{outline:2px solid var(--ot-focus-ring, #7c3aed);outline-offset:2px;}
.ot-toggle:disabled{opacity:0.55;cursor:not-allowed;}
@media (prefers-reduced-motion: reduce){
  .ot-toggle, .ot-toggle::after{transition:none;}
}
