/* Workspace controls follow the site theme; the exported bracket is always light. */
.tournament-app .tournament-main { max-width: 1600px; margin: 0 auto; padding: 28px 24px 60px; min-height: 0; }
.tournament-app .tournament-page button,
.tournament-app .tournament-page .button,
.tournament-app .tournament-page summary { font: inherit; }
.tournament-app .tournament-page button,
.tournament-app .button { min-height: 44px; padding: 10px 16px; border: 1px solid var(--input-border); border-radius: 8px; background: var(--input-bg); color: var(--text); cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.tournament-app .tournament-page button:disabled { opacity: .45; cursor: default; }
.tournament-app .tournament-page button:hover:not(:disabled), .tournament-app .button:hover { border-color: var(--accent); }
.tournament-app .tournament-page .primary { background: #08798b; color: white; border-color: #08798b; }
.tournament-app .tournament-page :focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.tournament-app .tournament-page input:not([type=checkbox]), .tournament-app .tournament-page textarea,
.tournament-app .tournament-page select { background: var(--input-bg); color: var(--text); border: 1px solid var(--input-border); border-radius: 8px; padding: 10px 12px; font: inherit; font-size: 16px; min-height: 44px; width: 100%; }
.tournament-app .tournament-page input[type=checkbox] { width: 18px; height: 18px; }
.tournament-app .tournament-page textarea { resize: vertical; line-height: 1.6; }
.tournament-app .tournament-page label { display: grid; gap: 8px; font-size: 14px; }
.tournament-app h1 { margin: 4px 0 12px; font-size: clamp(24px, 4vw, 32px); overflow-wrap: anywhere; }
.tournament-app h2 { font-size: 20px; margin: 0 0 20px; }
.tournament-app p { line-height: 1.6; }
.tournament-app [hidden] { display: none !important; }
.page-intro, .workspace-header { display: flex; justify-content: space-between; align-items: start; gap: 24px; margin-bottom: 28px; }
.page-intro p { color: var(--muted); margin-bottom: 0; }
.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .15em; }
.page-actions, .row-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.page-intro .page-actions { flex-shrink: 0; padding-top: 30px; }
.setup-panel { border-block: 1px solid var(--input-border); padding: 28px 0; margin: 28px 0; }
.setup-grid { display: grid; grid-template-columns: minmax(220px, 1fr) minmax(0, 1.5fr); gap: 24px; margin-top: 20px; }
.setup-grid > .setup-roster { grid-template-rows: auto 1fr; align-content: stretch; }
/* The pairing hint explains the prefix modes, so the example that demonstrates
   them is offered right there, under the explanation. */
.field-help-popover .field-action { margin-top: 12px; }
.tournament-app .tournament-page .field-action {
  min-height: 32px; padding: 4px 10px; font-size: 13px;
  background: transparent; border-color: var(--input-border); color: var(--accent);
}
.setup-fields { display: grid; align-content: start; gap: 16px; }
/* Taking the draw again belongs beside the mode that draws it. */
.pairing-row { display: flex; gap: 8px; }
.pairing-row select { flex: 1; min-width: 0; }
.preview-bracket { overflow: auto; max-height: 440px; background: var(--panel); }
.preview-bracket svg { max-width: none; }
.preview-bracket rect { pointer-events: none; }
/* A section's own action reads as part of its title, not as a second heading
   competing with it — so it is an icon beside the name. */
.section-title { display: flex; align-items: center; gap: 8px; }
.tournament-app .tournament-page .icon-btn { min-height: 32px; height: 32px; width: 32px; padding: 0; border-radius: 8px;
  color: var(--muted); background: transparent; }
.tournament-app .tournament-page .icon-btn:hover:not(:disabled) { color: var(--accent); }
.saved-section { margin: 36px 0; }
.section-heading { display: flex; justify-content: space-between; align-items: center; }
.saved-row { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 18px 0; border-bottom: 1px solid var(--input-border); }
.saved-row > div:first-child { min-width: 0; }
.saved-row h3 { margin: 0 0 8px; font-size: 17px; overflow-wrap: anywhere; }
.saved-row p, .empty-state, .muted { color: var(--muted); font-size: 14px; }
.saved-row p { margin: 0; }
.empty-state { padding: 32px 0; }
.row-actions { flex-shrink: 0; }
.official-downloads { border-top: 1px solid var(--input-border); padding-top: 20px; }

.workspace-header > div { min-width: 0; }
.workspace-header a { align-self: start; margin-bottom: 14px; }
.workspace-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; font-size: 14px; }
/* Carries a border because the badge also sits on --panel surfaces (the saved
   rows), where a --panel fill alone would make it invisible. */
.status-badge { display: inline-block; background: var(--bg); color: var(--text); font-weight: 600;
  border: 1px solid var(--input-border); border-radius: 6px; padding: 3px 8px; font-size: 12px;
  vertical-align: middle; white-space: nowrap; }
.status-badge[data-phase] { background: var(--panel); color: var(--muted); border-color: transparent; font-weight: 400; }
.status-badge[data-phase=underway] { background: var(--badge-live-bg); color: var(--badge-live-fg); }
.status-badge[data-phase=completed] { background: var(--badge-done-bg); color: var(--badge-done-fg); }
/* The one state that asks the user to act — the cloud copy, and therefore
   Live, is behind. It must not read as another neutral chip. */
.status-badge[data-state=unsynced] { background: var(--badge-warn-bg); color: var(--badge-warn-fg); border-color: var(--badge-warn-border); }
.tournament-app .tournament-page #save-state { font-size: 13px; min-height: 34px; padding: 6px 12px; color: var(--muted); }
/* No frame of its own. Zoomed past the panel width the bracket overflows it
   — a border and a rounded corner would be crossed by the table, and the
   sheet already carries its own border and shadow. The control bar's
   bottom edge is what separates the two. */
.tournament-app .bracket-wrap { position: relative; padding: 0; border: 0; border-radius: 0; background: var(--bg); overflow: visible; }
/* The Live view's share QR sits on the panel like the corner buttons, so it
   never covers a match — it carries its own white card because a QR has to
   stay black-on-white to scan in either theme. */
.live-qr { position: fixed; right: 16px; bottom: 16px; z-index: 95;
  padding: 6px; border-radius: 10px; background: #fff; box-shadow: 0 2px 10px #0000004d; line-height: 0; }
.live-qr img { display: block; width: 88px; height: 88px; }
@media (max-width: 640px) { .live-qr img { width: 64px; height: 64px; } }
/* One row at every width: the bracket is what needs the height, so nothing in
   the bar may wrap. Only the group tablist gives way — it scrolls sideways. */
/* One row at every width: the bracket is what needs the height. Nothing in the
   bar shrinks or wraps — when the row runs out of room it scrolls sideways, and
   the open menus are placed in viewport coordinates (view.js) so this scroll
   container cannot clip them. */
.bracket-controls { display: flex; flex-wrap: nowrap; align-items: center; gap: 10px; padding: 12px 16px;
  border-bottom: 1px solid var(--input-border); min-width: 0; }
/* Only the left half scrolls. The fixed cluster is a sibling of the scroller,
   never an overlay on top of it, so the group tablist can never slide beneath
   fullscreen and display settings can never end up past the right edge. */
.bracket-controls-scroll { display: flex; flex-wrap: nowrap; align-items: center; gap: 10px;
  flex: 1 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.bracket-controls-scroll::-webkit-scrollbar { display: none; }
.bracket-controls > :not(.bracket-controls-scroll), .bracket-controls-scroll > * { flex: 0 0 auto; }
/* One row, one button metric: the bar mixes plain buttons, a <summary> and a
   switch, which otherwise land on three different heights and baselines. */
.tournament-app .tournament-page .bracket-controls button,
.tournament-app .tournament-page .bracket-controls summary,
.tournament-app .tournament-page .bracket-controls .bracket-layout-switch {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 40px; height: 40px; padding: 0 14px; border-radius: 8px; font-size: 13px;
}
.tournament-app .tournament-page .bracket-controls .bracket-layout-switch { padding: 0; gap: 10px; }
/* Flex layout drops the disclosure marker, and without it the menu reads as a
   plain button — the header's menus keep theirs. */
.bracket-controls summary { list-style: none; }
.bracket-controls summary::-webkit-details-marker { display: none; }
/* One caret across the app — and drawn, not typed: the header's menus and the
   bar's carried two different glyphs (a chevron and a filled triangle), and the
   chevron character renders thin and sits low, like a stray comma. Two borders
   on an empty box give the same shape at any size, in the right place. */
.tournament-app .tournament-page summary::after {
  content: ''; flex: 0 0 auto; width: 6px; height: 6px; margin-left: 2px;
  border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted);
  transform: translateY(-2px) rotate(45deg);
}
.tournament-app .tournament-page details[open] > summary::after {
  transform: translateY(1px) rotate(-135deg);
}
/* …except the zoom mode, which is the middle segment of a stepper: a caret
   there breaks the symmetry of − | mode | + and made the group read as three
   different controls. The label itself changes with the mode, which is what
   says it can be pressed. */
.bracket-controls details.zoom-mode > summary::after { display: none; }
/* − , the mode menu and + are one segmented control: the mode sits between the
   two steppers, so the reading is where the size is changed. Only the group's
   own children are squared off — the buttons inside the mode panel are not. */
.zoom-controls { display: flex; align-items: center; flex: 0 0 auto; }
.zoom-controls > details { display: flex; }
/* One segmented control: every segment the same height and padding, whether it
   is a stepper, a mode menu (workspace, Live) or a mode reset (create). */
.tournament-app .tournament-page .zoom-controls > button,
.tournament-app .tournament-page .zoom-controls > details > summary {
  min-height: 40px; height: 40px; padding: 0 14px; font-size: 13px; font-variant-numeric: tabular-nums;
  /* The mode is a <summary> on the workspace and Live, a <button> on the create
     page: a summary has no button background of its own, which left the middle
     segment hollow between two filled steppers. */
  background: var(--input-bg); color: var(--text);
}
.tournament-app .tournament-page .zoom-controls > button:first-child,
.tournament-app .tournament-page .zoom-controls > button:last-child { padding: 0 16px; }
.tournament-app .tournament-page .zoom-controls > button,
.tournament-app .tournament-page .zoom-controls > details > summary { margin-left: -1px; border-radius: 0; }
.tournament-app .tournament-page .zoom-controls > button:first-child { border-radius: 8px 0 0 8px; margin-left: 0; }
.tournament-app .tournament-page .zoom-controls > button:not(:first-child):not(:last-child) { border-radius: 0; }
.tournament-app .tournament-page .zoom-controls > button:last-child { border-radius: 0 8px 8px 0; }
/* The open menu's own edge must not be painted over by the + it abuts. */
.zoom-controls > details[open] > summary { position: relative; z-index: 1; }
.tournament-app .bracket-controls label { display: flex; align-items: center; }
.bracket-controls .zoom-mode > div { left: 0; right: auto; }
/* The custom mode carries a percentage that changes on every step. */
.bracket-controls .zoom-mode summary { font-variant-numeric: tabular-nums; }
/* The tablist rides the bar's own scroll rather than nesting a second one. */
.tournament-app .bracket-controls .group-view-chips { margin: 0; padding: 0; flex-wrap: nowrap; }
.tournament-app .group-view-chips .group-chip { flex: 0 0 auto; }
.scroll-hint { color: var(--muted); font-size: 12px; padding: 0 16px; margin: 12px 0; }
/* The fold-away button only exists where there is a screen to win back. */
#toggle-controls { display: none; }
.bracket-wrap:fullscreen #toggle-controls,
.bracket-wrap.is-fullscreen #toggle-controls { display: inline-flex; }
.bracket-wrap.controls-collapsed #bracket-controls > *:not(.bracket-corner),
.bracket-wrap.controls-collapsed .bracket-corner > *:not(#toggle-controls) { display: none; }
.bracket-wrap.controls-collapsed #bracket-controls { border: 0; background: transparent; padding: 0; min-height: 0; }
/* Folded, the bar is gone from the flow entirely and only a tab is left on the
   screen's edge — the bracket is what the room is looking at, and the way back
   has to be somewhere that never covers a match. */
.bracket-wrap.controls-collapsed #toggle-controls {
  position: fixed; right: 0; top: 50%; transform: translateY(-50%); z-index: 96;
  min-width: 0; padding: 14px 6px; border-radius: 8px 0 0 8px; border-right: 0;
}
.bracket-wrap.controls-collapsed #toggle-controls svg { transform: rotate(180deg); }
/* Fullscreen is asked for to give the bracket the whole screen; the gesture
   hint has been read by then and is only taking a strip of it back. */
.tournament-app .bracket-wrap:fullscreen .scroll-hint,
.tournament-app .bracket-wrap.is-fullscreen .scroll-hint { display: none; }
/* Page-owned buttons, moved into the control bar by view.js and closing it on
   the right — apart from the controls that change what the bracket shows. */
/* Sticky so the bar can scroll sideways on a phone without taking fullscreen —
   the one control that must never be out of reach — off the right edge. */
/* Sticky, so scrolling the bar never takes fullscreen — the one control that
   must stay in reach — past the right edge. */
.bracket-corner { display: flex; gap: 8px; flex: 0 0 auto; margin-left: auto; padding-left: 12px; }
.tournament-app .tournament-page .bracket-corner-btn {
  display: flex; align-items: center; gap: 6px;
  min-height: 36px; padding: 6px 12px; border-radius: 8px; font-size: 13px;
  background: var(--bg); border: 1px solid var(--input-border); color: var(--text);
}
.tournament-app .tournament-page .bracket-corner-btn[disabled] { opacity: .55; }
/* The Live badge is the workspace's phase chip in another state: red dot while
   connected, plain muted chip for a snapshot, which is the one state not live. */
.status-badge.live-indicator { display: inline-flex; align-items: center; gap: 6px; color: var(--danger); }
.status-badge.live-indicator[data-state=snapshot] { color: var(--muted); }
.status-badge.live-indicator[data-state=snapshot] .live-dot { display: none; }
/* The bracket takes whatever the page has left after the header and the bar,
   rather than sitting in a fixed box with the panel empty below it. Only the
   bracket scrolls; the page itself does not. */
/* Fill the window rather than guess the shared header's height: the body is the
   column, the page takes what is left under the header, and the panel takes what
   is left under the title block. */
body.tournament-app:has(.bracket-wrap) { display: flex; flex-direction: column; min-height: 100dvh; }
body.tournament-app:has(.bracket-wrap) > .tournament-page { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.tournament-app .tournament-main:has(> .bracket-wrap) { flex: 1; display: flex; flex-direction: column;
  min-height: 0; max-width: none; margin-inline: 0; padding: 12px 8px; }
/* The bracket is wider than a phone, and auto side margins (plus a flex item's
   refusal to shrink below its content) would let that width stretch the header
   and the bar until their right-hand controls sat off-screen. Only the bracket
   itself may overflow — the page scrolls to reach it. */
body.tournament-app:has(.bracket-wrap) > .tournament-page,
.tournament-app .tournament-main:has(> .bracket-wrap),
.tournament-app .tournament-main > .bracket-wrap,
.tournament-app .bracket-wrap > .bracket { min-width: 0; }
/* Header of a page whose job is the bracket: tight enough that the table starts
   near the top of the screen. */
.tournament-main:has(> .bracket-wrap) .workspace-header { margin-bottom: 16px; }
.tournament-main:has(> .bracket-wrap) .workspace-header a { margin-bottom: 0; }
/* Names the identity block rather than every child <div>: the header also
   holds the help popover, and a layout rule that reaches it overrides the
   UA's [popover]:not(:popover-open){display:none} — the bubble then shows
   unprompted, which is how it surfaced when a devtools resize reflowed the
   header out from under it. */
.tournament-main:has(> .bracket-wrap) .workspace-header > .workspace-identity { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; }
.tournament-main:has(> .bracket-wrap) .workspace-header h1 { margin: 0; }
.tournament-main:has(> .bracket-wrap) .workspace-header .muted { margin: 0; flex-basis: 100%; }
.tournament-main:has(> .bracket-wrap) .workspace-header .muted { margin: 8px 0 0; }
/* The gesture hint is a one-line aside, not a band of its own. */
.tournament-main:has(> .bracket-wrap) .scroll-hint { margin: 8px 0; }
.tournament-app .tournament-main > .bracket-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; }
/* Sideways panning happens inside the panel; the page still scrolls down.
   Letting the bracket overflow the page horizontally made the document wider
   than the layout viewport, and a phone browser answers that by shrinking the
   whole page to fit — the bracket looked huge and the header, the bar and
   everything else went tiny around it. Vertically the panel still takes the
   height its content needs (so overflow-y never triggers) and a table taller
   than the window is pulled with the page, not inside a box. */
.tournament-app .bracket-wrap > .bracket { flex: 0 0 auto; min-height: min(60dvh, 460px); }
.tournament-app .bracket { display: block; overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;
  width: 100%; padding: 12px; background: transparent; }
.tournament-app .bracket svg { border: 1px solid var(--input-border); border-radius: 8px; box-shadow: 0 2px 10px rgb(0 0 0 / .28); }
.tournament-app .bracket::after { display: none; }
.tournament-app .bracket svg { display: block; min-width: 0; max-width: none; margin: 0; flex-shrink: 0; }
.tournament-app .bracket rect[aria-pressed=true] { stroke: #128578; stroke-width: 2; }
.action-menu { position: relative; }
.action-menu summary { cursor: pointer; border: 1px solid var(--input-border); border-radius: 8px; padding: 11px 16px; min-height: 44px; }
.action-menu > div { position: absolute; right: 0; top: calc(100% + 6px); padding: 8px; background: var(--bg); border: 1px solid var(--input-border); border-radius: 8px; z-index: 20; min-width: 220px; box-shadow: 0 6px 20px #0002; display: grid; gap: 6px; }
/* The roster dialog is taller than a phone; scrolled to its end it handed the
   rest of the gesture to the bracket behind it. */
.tournament-dialog { color: var(--text); background: var(--bg); border: 1px solid var(--input-border); border-radius: 12px; padding: 24px; width: min(680px, calc(100vw - 24px)); max-height: 88dvh; overflow: auto; overscroll-behavior: contain; }
.tournament-dialog::backdrop { background: #101c2d99; }
.tournament-dialog form, .dialog-body { display: grid; gap: 16px; }
.tournament-dialog h2 { margin: 0; }
/* The body is a grid, so a paragraph's own margins add to the gap instead of
   collapsing into it — three blocks then read as three separate screens. */
.tournament-dialog .dialog-body p { margin: 0; }
.tournament-dialog .dialog-body input[type=number] { width: 120px; }
/* A field whose label carries a help button is a heading + control pair, so it
   keeps the label's own spacing rather than the body's block gap. */
.tournament-dialog .dialog-body .field-heading + select { margin-top: -10px; }
.tournament-dialog .dialog-actions { display: flex; justify-content: end; gap: 8px; margin: 0; }
/* Share dialog: the link is shown, not just copied — and the QR is how it gets
   onto a phone at the venue. The code keeps its own white quiet zone in both
   themes, because that is what a scanner needs. */
.share-note { margin: 0; color: var(--muted); }
.tournament-dialog .share-url { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.share-qr { display: flex; justify-content: center; }
.share-qr canvas { background: #fff; padding: 10px; border-radius: 10px; width: 190px; height: 190px; }
/* The pressed button reports what happened — visible wherever it sits. */
.tournament-app .tournament-page button.is-done { color: var(--accent); border-color: var(--accent); }
/* A link's own actions close its block on the right, the same way in both link
   dialogs. Copy is the one people press, so it carries the accent tint;
   replacing a judge link locks someone out, so it is marked as destructive. */
.link-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
.tournament-app .tournament-page .copy-btn,
.tournament-app .tournament-page #judge-links {
  background: var(--btn-secondary-bg); color: var(--btn-secondary-text); border-color: var(--btn-secondary-border);
}
.tournament-app .tournament-page .copy-btn:hover:not(:disabled),
.tournament-app .tournament-page #judge-links:hover:not(:disabled) {
  background: var(--btn-secondary-hover-bg); border-color: var(--btn-secondary-hover-border);
}
/* Changing the group count kills every judge link already handed out, and
   redrawing the roster clears the results: both undo work other people have
   already done, so they sit together at the menu's foot on a tinted ground
   rather than each carrying its own warning colour. */
.action-menu > div .menu-danger {
  display: grid; gap: 6px; padding: 8px; margin-top: 2px;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  border-radius: 8px; background: color-mix(in srgb, var(--danger) 8%, transparent);
}
.tournament-app .tournament-page .danger-btn { color: var(--danger); border-color: var(--danger); background: transparent; }
.tournament-app .tournament-page .danger-btn:hover:not(:disabled) { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 14%, transparent); }
.tournament-dialog .share-open { flex: 0 0 auto; }
/* A dialog's own warning line: the form still applies, so this says what the
   apply will do rather than refusing it. */
.tournament-dialog .form-warning { margin: 0; color: var(--danger); font-size: 13px; }
.tournament-dialog .form-warning[hidden] { display: none; }
.full-name-list { padding-left: 32px; }
.full-name-list li { overflow-wrap: anywhere; line-height: 1.7; padding: 8px; border-bottom: 1px solid var(--input-border); }
/* iOS Safari implements the Fullscreen API for <video> only, so on the phone
   the button falls back to a fixed overlay (view.js `fullscreen`). Both
   states paint the same. */
.tournament-app .bracket-wrap:fullscreen,
.tournament-app .bracket-wrap.is-fullscreen { border: 0; border-radius: 0; background: var(--bg); overflow: auto;
  display: flex; flex-direction: column; }
.tournament-app .bracket-wrap.is-fullscreen { position: fixed; inset: 0; z-index: 90; }
/* The page behind must not scroll under the overlay, and on iOS a scrolled
   page would otherwise show through above it. */
body.tournament-app.is-bracket-fullscreen { overflow: hidden; }
@media (max-width: 700px) {
  .tournament-app .tournament-main { padding: 20px 12px 40px; }
  .page-intro, .workspace-header { flex-direction: column; gap: 12px; }
  .page-intro .page-actions { padding-top: 0; }
  .setup-grid { grid-template-columns: 1fr; }
  .saved-row { align-items: start; flex-direction: column; gap: 12px; }
  .bracket-controls { padding: 10px 12px; gap: 8px; }
  .tournament-app .bracket { padding: 6px; }
  .tournament-app .tournament-main:has(> .bracket-wrap) { padding-inline: 6px; }
  .action-menu > div { right: auto; left: 0; }
}
/* The fixed cluster drops its labels before the row gets tight: ⚙ and the
   fullscreen glyph are self-evident, both keep their aria-label, and the ~120px
   saved goes to the group tablist so it scrolls later. */
@media (max-width: 900px) {
  .bracket-controls .view-more .menu-label,
  .tournament-app .tournament-page .bracket-corner-btn span { display: none; }
  /* With the label gone the button is the gear, which already says a menu
     hangs off it — a caret beside it is one glyph too many. */
  .bracket-controls details.view-more > summary::after { display: none; }
}

@media print {
  .tournament-app #sharedHeader, .tournament-app dialog { display: none !important; }
  .tournament-app .tournament-main { padding: 0; min-height: 0; }
  .tournament-app .bracket { overflow: visible !important; max-height: none !important; height: auto !important; }
  .tournament-app .bracket svg { width: 100% !important; height: auto !important; max-width: 100% !important; }
}

@media (max-width: 700px) {
  .workspace-header { margin-bottom: 16px; gap: 8px; }
  .workspace-header a { margin-bottom: 6px; }
  .workspace-header h1 { margin-bottom: 8px; }
  .workspace-header #phase-hint { margin: 8px 0; }
  .workspace-header .page-actions { gap: 6px; }
  .tournament-app .workspace-header .page-actions > button,
  .workspace-header .action-menu summary { padding-inline: 10px; font-size: 14px; }
  /* Everything in the bar trims to its shortest honest form so the row still
     fits a 360 px phone: icons lose their labels, the switch keeps the label
     that names its on state (the aria-label carries the rest). */
  .bracket-controls { gap: 6px; }
  .tournament-app .tournament-page .zoom-controls > button,
  .tournament-app .tournament-page .zoom-controls > details > summary { padding-inline: 10px; }
  .tournament-app .tournament-page .bracket-controls summary { padding-inline: 10px; font-size: 12px; }
  .tournament-app .tournament-page .bracket-controls .bracket-layout-switch { padding-inline: 6px; gap: 6px; }
  .tournament-app .tournament-page .layout-toggle > button { padding-inline: 10px; }
  .bracket-controls .view-more .menu-label,
  .tournament-app .tournament-page .bracket-corner-btn span { display: none; }
  .tournament-app .tournament-page .bracket-corner-btn { padding-inline: 9px; }
  .bracket-controls { gap: 6px; padding: 8px; }
}

/* Management page: one centre axis — menu, saved records, printable templates. */
.tournament-manager .tournament-main { max-width: 860px; padding-top: 52px; }
.tournament-manager .page-intro { flex-direction: column; align-items: center; text-align: center; gap: 0; margin-bottom: 32px; }
.tournament-manager .page-intro h1 { margin-bottom: 10px; }
.tournament-manager .eyebrow { color: var(--muted); margin: 0 0 10px; }
.start-actions { display: grid; gap: 12px; width: min(100%, 440px); margin: 0 auto 52px; }
.tournament-app .start-card { display: flex; align-items: center; justify-content: center; gap: 14px; padding: 20px 24px; min-width: 0; min-height: 86px; text-align: center; text-decoration: none; color: var(--text); background: var(--bg); border: 1px solid var(--input-border); border-radius: 12px; }
.tournament-app .start-new { border-color: #13899a; background: color-mix(in srgb, var(--bg) 94%, #13899a); }
.start-card > span:nth-child(2) { min-width: 0; }
.start-card strong { display: block; font-size: 17px; }
.start-card small { display: block; font-size: 14px; color: var(--muted); overflow-wrap: anywhere; margin-top: 5px; }
.start-icon { font-size: 24px; color: #13899a; }
.start-arrow { display: none; }
/* The start menu is centred, but a panel of records is a reading surface: each
   section is its own card and each record its own row card, so the eye lands on
   the boundaries rather than one centred column of similar-sized text. */
.saved-columns { display: grid; gap: 28px; margin-bottom: 44px; }
.tournament-manager .saved-section { margin: 0; min-width: 0; padding: 20px; text-align: left;
  background: var(--bg); border: 1px solid var(--input-border); border-radius: 12px; }
.tournament-manager .section-heading { align-items: start; gap: 16px; margin-bottom: 16px; text-align: left; }
.tournament-manager .section-heading h2 { font-size: 18px; margin: 0 0 4px; }
.tournament-manager .section-heading p { margin: 0; }
.tournament-manager .section-heading > button { flex-shrink: 0; }
.tournament-manager .saved-list { display: grid; gap: 10px; }
.tournament-manager .saved-list > button { justify-self: start; }
.tournament-manager .empty-state { margin: 0; padding: 22px; text-align: center;
  background: var(--panel); border-radius: 10px; }
.tournament-manager .saved-row { flex-wrap: wrap; padding: 14px 16px; border-bottom: 0;
  background: var(--panel); border-radius: 10px; }
.tournament-manager .saved-row > div:first-child { flex: 1 1 210px; }
.tournament-manager .saved-row h3 { font-size: 16px; margin-bottom: 6px; }
.tournament-manager .saved-row p { font-size: 12px; }
.tournament-manager .saved-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
.tournament-manager .saved-row h3 { font-size: 18px; margin-bottom: 12px; }
.saved-meta { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 14px; line-height: 1.6; }
.tournament-manager .saved-updated { margin-top: 8px; line-height: 1.6; display: grid; gap: 2px; }
.tournament-manager .saved-row .row-actions { border-top: 1px solid var(--input-border); padding-top: 12px; justify-content: flex-end; }
.tournament-manager .saved-row .row-actions > :first-child { border-color: var(--accent); color: var(--accent); }
@media (max-width: 700px) {
  .tournament-manager .saved-row .row-actions { display: grid; grid-template-columns: 1fr 1fr; }
  .tournament-manager .saved-row .row-actions > * { text-align: center; }
}
.tournament-manager .official-downloads { padding-top: 28px; }
.official-source { color: var(--accent); font-size: 14px; text-underline-offset: 3px; padding-block: 10px; flex-shrink: 0; }
.template-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.template-card { min-width: 0; border: 1px solid var(--input-border); border-radius: 10px; overflow: hidden; background: var(--bg); }
.template-preview { display: block; background: var(--panel); padding: 14px; border-bottom: 1px solid var(--input-border); }
.template-preview img { display: block; width: 100%; height: auto; aspect-ratio: 420 / 297; object-fit: contain; }
.template-preview:hover { background: var(--panel-soft); }
.template-body { padding: 16px; }
.template-card h3 { display: flex; align-items: baseline; gap: 6px; margin: 0 0 12px; }
.template-card h3 > span { font-size: 28px; line-height: 1; font-variant-numeric: tabular-nums; }
.template-card h3 small { font-size: 14px; font-weight: 400; color: var(--muted); }
.template-formats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.template-formats a { display: flex; justify-content: center; align-items: center; min-height: 44px; border: 1px solid var(--input-border); border-radius: 6px; font-size: 13px; font-weight: 600; text-decoration: none; color: var(--text); }
.template-formats a:first-child { color: #08798b; background: color-mix(in srgb, var(--bg) 92%, #13899a); border-color: #13899a; }
.template-formats a:hover { border-color: var(--accent); }
.template-note { margin-top: 18px; }
@media (max-width: 800px) {
  .template-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}
@media (max-width: 700px) {
  .tournament-manager .tournament-main { padding-top: 28px; }
  .start-actions { margin-bottom: 32px; }
  .tournament-app .start-card { padding: 18px; min-height: 80px; }
  .start-card strong { font-size: 16px; }
  /* The row stacks here, so the row-direction basis would become a tall gap. */
  .tournament-manager .saved-row > div:first-child { flex: 0 1 auto; }
  .tournament-manager .saved-section { padding: 16px; }
  .tournament-manager .section-heading { flex-direction: column; gap: 12px; }
  .template-preview { padding: 8px; }
  .template-body { padding: 12px; }
  .template-card h3 { flex-wrap: wrap; }
  .template-card h3 > span { font-size: 24px; }
}
@media (max-width: 380px) { .template-grid { grid-template-columns: 1fr; } }

.tournament-manager[data-screen="menu"] .tournament-main { min-height: calc(100dvh - 90px); display: flex; flex-direction: column; justify-content: center; }
.manager-back { display: flex; margin: 0 0 20px; }
.tournament-app .tournament-page .manager-back button,
.tournament-app .tournament-page .workspace-header a { font-size: 14px; }
.tournament-manager .setup-panel { margin-top: 0; }
@media (hover: hover) {
  .tournament-manager .start-card:hover { border-color: var(--accent); background: var(--panel); }
}

.tournament-app .zoom-mode > div button[aria-pressed=true] { border-color: var(--accent); background: var(--panel); color: var(--accent); }

.setup-field { display: grid; align-content: start; gap: 8px; min-width: 0; }
/* Match the compact circular help control and native popover in the part modal. */
.field-heading { display: flex; align-items: center; gap: 8px; }
.tournament-app .tournament-page .field-help {
  width: 1.6em; height: 1.6em; min-width: 0; min-height: 0; padding: 0;
  border-radius: 50%; border: 1px solid var(--input-border); background: transparent;
  color: var(--muted); font-size: 12px; font-weight: 700; line-height: 1; flex-shrink: 0;
}
@media (hover: hover) {
  .tournament-app .tournament-page .field-help:hover { color: var(--accent); border-color: var(--accent); }
}
.field-help-popover {
  margin: auto; border: 1px solid var(--input-border); background: var(--bg); color: var(--text);
  border-radius: 12px; padding: 16px 18px; box-shadow: var(--viewer-dialog-shadow);
  width: min(360px, 90vw); max-height: 80vh; overflow-y: auto; font-size: 14px;
}
.field-help-popover::backdrop { background: rgb(0 0 0 / .35); }
/* A closed popover stays closed whatever a layout rule says. */
.tournament-app [popover]:not(:popover-open) { display: none; }
.tournament-app .field-help-popover h2 { font-size: 14px; margin: 0 0 6px; }
.field-help-popover p { margin: 0; white-space: pre-line; }
.field-help-popover .help-lines { display: grid; gap: 10px; }
/* The closing line is not one of the modes — it is how prefixes are written. */
.field-help-popover .help-lines .help-note { padding-top: 10px; border-top: 1px solid var(--input-border); color: var(--muted); }

/* The settings menu mixes label/control rows with plain buttons, so the rows
   run the full width of the menu and keep their control on the trailing edge. */
.action-menu > div .menu-field { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; min-height: 44px; padding-inline: 6px; font-size: 14px; white-space: nowrap; cursor: pointer; }
/* Group count carries the same "?" as the create form: it decides the judge
   links, not just the bands, so the menu has to say what it does. */
.action-menu > div .menu-field .field-heading label { cursor: pointer; }
.action-menu > div .menu-field input[type=number] { width: 80px; }
/* A menu row that turns one thing on or off reads as a switch, like the ones on
   the bars — a bare checkbox in a row of buttons looked like a form control that
   had wandered in. */
.tournament-app .tournament-page .menu-field input[type=checkbox] {
  appearance: none; -webkit-appearance: none; position: relative; flex: 0 0 auto;
  width: 42px; height: 24px; min-height: 0; margin: 0; cursor: pointer;
  border: 1px solid var(--input-border); border-radius: 24px; background: var(--panel);
}
.tournament-app .tournament-page .menu-field input[type=checkbox]::after {
  content: ''; position: absolute; left: 3px; top: 3px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--muted); transition: transform .15s ease, background .15s ease;
}
.tournament-app .tournament-page .menu-field input[type=checkbox]:checked {
  background: var(--accent); border-color: var(--accent);
}
.tournament-app .tournament-page .menu-field input[type=checkbox]:checked::after {
  transform: translateX(18px); background: var(--viewer-accent-contrast, var(--bg));
}
.tournament-app .tournament-page .menu-field input[type=checkbox]:focus-visible {
  outline: 3px solid var(--accent); outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .tournament-app .tournament-page .menu-field input[type=checkbox]::after { transition: none; }
}
.tournament-app .action-menu > div .bracket-layout-switch { display: flex; justify-content: space-between; padding-inline: 6px; }

/* Same segmented shape as the zoom stepper next to it; the pressed segment
   carries the accent, which is what says which direction is on. */
.layout-toggle { display: inline-flex; flex: 0 0 auto; }
.tournament-app .tournament-page .layout-toggle > button {
  min-height: 40px; height: 40px; padding: 0 14px; font-size: 13px;
  border-radius: 0; margin-left: -1px; white-space: nowrap;
}
.tournament-app .tournament-page .layout-toggle > button:first-child { border-radius: 8px 0 0 8px; margin-left: 0; }
.tournament-app .tournament-page .layout-toggle > button:last-child { border-radius: 0 8px 8px 0; }
.tournament-app .tournament-page .layout-toggle > button[aria-pressed=false] { color: var(--muted); }
.tournament-app .tournament-page .layout-toggle > button[aria-pressed=true] {
  border-color: var(--accent); background: var(--accent); color: var(--viewer-accent-contrast, var(--bg)); position: relative; z-index: 1;
}
.tournament-app .tournament-page .bracket-layout-switch {
  display: inline-flex; position: relative; align-items: center; gap: 10px;
  min-height: 44px; cursor: pointer; font-size: 14px; white-space: nowrap;
}
.tournament-app .tournament-page .bracket-layout-switch input {
  position: absolute; width: 1px; height: 1px; min-height: 0; opacity: 0; margin: 0;
}
/* Decoration only: the real control is the visually-hidden input behind it, and
   an intercepted pointer event is a click the switch never receives. */
.layout-switch-track { pointer-events: none; position: relative; width: 42px; height: 24px; flex-shrink: 0;
  border: 1px solid var(--input-border); border-radius: 24px; background: var(--panel); }
.layout-switch-track::after { content: ''; position: absolute; left: 3px; top: 3px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--muted); }
.bracket-layout-switch input:checked + .layout-switch-track { background: var(--accent); border-color: var(--accent); }
.bracket-layout-switch input:checked + .layout-switch-track::after { transform: translateX(18px); background: var(--viewer-accent-contrast, var(--bg)); }
.bracket-layout-switch input:focus-visible + .layout-switch-track { outline: 3px solid var(--accent); outline-offset: 3px; }
@media (min-width: 701px) {
  .setup-grid > .setup-roster { grid-template-rows: auto minmax(0, 1fr); min-height: 0; contain: size; }
  .tournament-app .tournament-page .setup-roster textarea { height: 100%; min-height: 0; }
}

/* Two switches are not worth a menu when the preview is the thing being looked
   at: they sit on the toolbar itself, as they do above the live bracket. */
/* The preview is one labelled panel: its controls ride the panel's own top
   row, so they read as belonging to the table under them rather than to the
   form above. */
.preview-label { font-size: 14px; }
.preview-frame { border: 1px solid var(--input-border); border-radius: 10px; overflow: hidden;
  background: var(--panel); margin: 0 0 8px; }
.preview-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 12px; padding: 8px 12px; border-bottom: 1px solid var(--input-border); }

.tournament-app .tournament-page .preview-bracket svg { display: block; max-width: none; }
/* The preview reads top-down and ends at its confirm, so the button sits at the
   end of the line the display switches started. */
#preview-panel { display: grid; justify-items: end; gap: 8px; margin-top: 28px; }
#preview-panel > .preview-label { justify-self: start; }
#preview-panel > .preview-frame { justify-self: stretch; }
.setup-display-options { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; }
.setup-display-options .bracket-layout-switch { display: flex; gap: 10px; }
.bracket-controls > .view-more { margin-left: auto; }
.bracket-controls > .view-more + .bracket-corner { margin-left: 0; }
.preview-bracket { border-radius: 12px; }
.tournament-app .bracket-wrap:fullscreen .bracket svg,
.tournament-app .bracket-wrap.is-fullscreen .bracket svg { border-radius: 0; }

/* Editing workspace: keep navigation quiet and the bracket close to the title. */
.tournament-editor #sharedHeader .app-header { padding: 12px 20px; gap: 0; border-bottom: 1px solid var(--viewer-input-border); }
.tournament-editor #sharedHeader .header-actions { display: none; }
.tournament-editor #sharedHeader h1 { font-size: 20px; margin: 0; }
.tournament-editor .tournament-main:has(> .bracket-wrap) { padding: 16px 24px 24px; }
.workspace-back { margin-bottom: 16px; }
.workspace-back a { display: inline-flex; align-items: center; gap: 8px; min-height: 32px; font-size: 13px; color: var(--muted); text-decoration: none; }
.tournament-editor .tournament-main .workspace-header { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: start; gap: 16px 24px; margin-bottom: 20px; }
.tournament-editor .tournament-main .workspace-header > .workspace-identity { display: grid; gap: 10px; }
.tournament-editor .workspace-header #workspace-title { font-size: 24px; line-height: 1.3; font-weight: 700; }
.tournament-editor .workspace-meta { gap: 8px 12px; font-size: 13px; }
.tournament-editor .tournament-main .workspace-header #phase-hint { margin: 0; font-size: 12px; line-height: 1.5; }
.tournament-editor .tournament-page #save-state { border-color: transparent; background: transparent; padding: 0 4px; min-height: 28px; font-size: 12px; }
.tournament-editor .tournament-main .workspace-header > .page-actions { display: flex; flex-wrap: nowrap; gap: 8px; }
.tournament-editor .workspace-header .page-actions > button,
.tournament-editor .workspace-header summary { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 40px; padding: 8px 12px; font-size: 13px; white-space: nowrap; }
.tournament-editor .workspace-header summary { list-style: none; background: transparent; }
.tournament-editor .workspace-header summary::-webkit-details-marker { display: none; }

.tournament-editor .tournament-page #share-live { background: var(--viewer-accent); color: var(--viewer-accent-contrast); border-color: var(--viewer-accent); font-weight: 600; }
.tournament-editor .workspace-header .action-menu > div { font-size: 14px; }
.tournament-editor .bracket svg { box-shadow: none; }
@media (hover: hover) {
  .workspace-back a:hover { color: var(--accent); }
  .tournament-editor .workspace-header summary:hover { border-color: var(--accent); }
}
@media (max-width: 1000px) {
  .tournament-editor .tournament-main .workspace-header { grid-template-columns: minmax(0, 1fr); gap: 14px; }
}
@media (max-width: 700px) {
  .tournament-editor #sharedHeader .app-header { padding: 10px 12px; }
  .tournament-editor #sharedHeader h1 { font-size: 18px; }
  .tournament-editor .tournament-main:has(> .bracket-wrap) { padding: 12px 10px 20px; }
  .workspace-back { margin-bottom: 10px; }
  .tournament-editor .workspace-header #workspace-title { font-size: 22px; }
  .tournament-editor .tournament-main .workspace-header > .page-actions { gap: 6px; flex-wrap: wrap; }
  .tournament-editor .workspace-header .page-actions > button,
  .tournament-editor .workspace-header summary { min-height: 44px; padding: 8px 10px; }
  .tournament-editor .workspace-settings > div { right: 0; left: auto; }
}
