/* Design tokens. Every colour a theme may want to change is a variable here,
   with the default look as its value — a theme (viewer/themes/<name>.css,
   loaded after this file via ?theme=) overrides them on :root, plus any
   structural rules it needs. Prefixes: --ui-* the stock viewer chrome
   (toolbar, settings, help, tooltip), --ann-* the annotation side panel,
   --fp-* the floor plan. --hotspot-color / --hotspot-hover are deliberately
   NOT defined by default: index.js reads them for the 3D hotspot sprites and
   keeps its built-in colours when they are absent. Likewise --hotspot-display:
   none hides the 3D hotspots entirely (points stay reachable via panel/map). */
:root {
  --canvas-opacity: 1;

  /* viewer chrome */
  --ui-text: #E0DCDD;
  --ui-text-strong: #fff;
  --ui-muted: #AAA;
  --ui-accent: #F60;
  --ui-accent-soft: rgba(255, 102, 0, 0.35);
  --ui-icon-outline: black;
  --ui-group-bg: rgba(0, 0, 0, 0.3);
  --ui-group-radius: 6px;
  --ui-surface: rgba(51, 51, 51, 0.8);
  --ui-surface-solid: #282828;
  --ui-border: rgba(255, 255, 255, 0.08);
  --ui-hint-bg: rgba(0, 0, 0, 0.7);
  --ui-loading-bg: #fff;
  --ui-loading-track: rgba(0, 0, 0, 0.08);

  /* annotation panel. The palette is monochrome like the outer page's header;
     --ann-accent is the single emphasis colour (active row, links). */
  --ann-text: #1c1c1c;
  --ann-body-text: #333;
  --ann-muted: #666666;
  --ann-line: #e0e0e0;
  --ann-hairline: #efefef;
  --ann-accent: #1c1c1c;
  --ann-accent-text: #fff;
  --ann-hover: #f5f6f7;
  --ann-active: #f2f3f4;
  --ann-surface: #fff;
  --ann-head-bg: #fafafa;
  --ann-border: #c9ccd0;
  --ann-button-hover: #f0f0f0;
  --ann-scrollbar: #d5d7da;
  --ann-dot-text: #7b8085;
  --ann-dot-bg: #e4e6e8;
  --ann-link-text: #fff;
  --ann-link-bg: var(--ann-text);
  --ann-link-border: transparent;
  --ann-link-hover-text: #fff;
  --ann-link-hover-bg: #3a3a3a;
  --ann-panel-radius: 14px;

  /* floor plan. The marker is the one deliberately non-monochrome colour: it
     has to read as "not part of the drawing" against black-on-white linework. */
  --fp-marker: #ff6600;
  --fp-marker-stroke: #fff;
  --fp-pin: var(--ann-accent);
  --fp-pin-stroke: #fff;
  --fp-pin-text: #fff;
  --fp-pin-active: #9aa0a6;
  --fp-minimap-bg: #fff;
  --fp-minimap-border: var(--ann-border);
  --fp-credit-bg: var(--ann-head-bg);
  --fp-credit-text: var(--ann-muted);
  --fp-credit-hover: var(--ann-text);
  --fp-toggle-bg: var(--fp-minimap-bg);
  --fp-toggle-text: var(--ann-text);
  --fp-toggle-hover-bg: var(--fp-credit-bg);

  /* project briefing modal (settings.json -> briefing). Hidden entirely
     (#briefingModal stays .hidden) on a scene without one, so these are
     dormant unless a project opts in. */
  --lang-menu-bg: #fff;
  --lang-menu-text: #1c1c1c;
  --lang-menu-hover: #f0f0f0;
  --lang-menu-border: rgba(0, 0, 0, 0.14);
  --lang-menu-accent: #1c1c1c;
  --brief-overlay-bg: rgba(0, 0, 0, 0.55);
  --brief-bg: rgba(255, 255, 255, 0.95);
  --brief-text: #1c1c1c;
  --brief-body-text: #333;
  --brief-muted: #666;
  --brief-border: rgba(0, 0, 0, 0.14);
  --brief-head-bg: var(--ann-head-bg);
  --brief-head-text: var(--ann-text);
  --brief-accent: var(--ann-accent);
  --brief-accent-text: var(--ann-accent-text);
  --brief-cta-bg: var(--ann-text);
  --brief-cta-text: #fff;
  --brief-cta-hover-bg: #3a3a3a;
  --brief-kbd-bg: var(--ann-text);
  --brief-kbd-text: #fff;
  --brief-radius: var(--ann-panel-radius);
}

* {
  margin: 0;
  padding: 0;
  touch-action: none;
}

body {
  overflow: hidden;
  font-family: "Lato", Arial, sans-serif;
  -moz-user-select: none;
       user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

canvas {
  opacity: var(--canvas-opacity);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

body::after {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  backdrop-filter: blur(1px);
  -webkit-backdrop-filter: blur(1px);
  pointer-events: none;
}

button {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
}

.hidden {
  display: none !important;
}

.spacer {
  flex-grow: 1;
}

#ui {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

#ui * {
  pointer-events: auto;
}

.walkHint {
  position: absolute;
  top: max(60px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  background-color: var(--ui-hint-bg);
  color: var(--ui-text-strong);
  font-size: 14px;
  padding: 10px 20px;
  border-radius: 8px;
  cursor: pointer;
  pointer-events: auto;
  white-space: nowrap;
}

/* branding link for third-party embeds */
#supersplatBranding {
  position: absolute;
  top: max(16px, env(safe-area-inset-top));
  left: max(16px, env(safe-area-inset-left));
  display: flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  color: #fff;
}
#supersplatBranding > svg {
  height: 16px;
  width: auto;
  flex-shrink: 0;
  padding: 8px;
  border-radius: 6px;
  background-color: rgba(0, 0, 0, 0.3);
}
#supersplatBranding > svg > g.stroke {
  fill: black;
  stroke: black;
  stroke-width: 2;
  stroke-linejoin: round;
  opacity: 0.4;
}
#supersplatBranding > svg > g.fill {
  fill: currentColor;
  stroke: none;
}
#supersplatBranding > span {
  font-size: 14px;
  font-weight: bold;
  text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.4), 1px -1px 0 rgba(0, 0, 0, 0.4), -1px 1px 0 rgba(0, 0, 0, 0.4), 1px 1px 0 rgba(0, 0, 0, 0.4);
}

/* poster */
#poster {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: var(--poster-url);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* loadingWrap: full-screen backdrop shown until the first frame renders */
#loadingWrap {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: var(--ui-loading-bg);
}
#loadingWrap > #loadingBar {
  position: relative;
  width: min(240px, 60vw);
  height: 6px;
  margin-top: 12px;
  border-radius: 3px;
  overflow: hidden;
  background-color: var(--ui-loading-track);
}
/* Indeterminate sweep: octree/LOD streaming has no reliable 0-100% signal, so this
   animates unconditionally instead of reflecting (unreliable) real progress. */
#loadingWrap > #loadingBar::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -35%;
  width: 35%;
  border-radius: 3px;
  background-color: var(--ui-accent);
  animation: loadingBarSweep 1.1s ease-in-out infinite;
}
@keyframes loadingBarSweep {
  0% { left: -35%; }
  100% { left: 100%; }
}

/* controlsWrap */
#controlsWrap {
  position: absolute;
  left: max(16px, env(safe-area-inset-left));
  right: max(16px, env(safe-area-inset-right));
  bottom: max(16px, env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
}
#controlsWrap.faded-in {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.5s ease-out;
}
#controlsWrap.faded-out {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s 0.5s, opacity 0.5s ease-out;
}
#controlsWrap > #timelineContainer {
  height: 30px;
  cursor: pointer;
}
#controlsWrap > #timelineContainer > #line {
  width: 100%;
  height: 50%;
  border-bottom: 4px solid #d9d9d9;
}
#controlsWrap > #timelineContainer > #handle {
  position: absolute;
  top: 16.5px;
  width: 12px;
  height: 12px;
  transform: translate(-50%, -50%);
  border: 2px solid #d9d9d9;
  border-radius: 50%;
  background-color: #FFAF50;
}
#controlsWrap > #timelineContainer > #time {
  position: absolute;
  top: 0;
  padding: 2px 4px;
  transform: translate(-50%, -100%);
  font-size: 12px;
  border-radius: 4px;
  color: #fff;
  background-color: rgba(40, 40, 40, 0.5);
}
#controlsWrap > #buttonsContainer {
  display: flex;
  gap: 8px;
}
#controlsWrap > #buttonsContainer .buttonGroup {
  display: flex;
  background-color: var(--ui-group-bg);
  border-radius: var(--ui-group-radius);
}
#controlsWrap > #buttonsContainer .buttonGroup:not(:has(> :not(.hidden))) {
  display: none;
}
#controlsWrap > #buttonsContainer {
  /* controlButton */
}
#controlsWrap > #buttonsContainer .controlButton {
  width: 34px;
  height: 34px;
  padding: 0;
  margin: 0;
  border: 0;
  cursor: pointer;
  color: var(--ui-text);
  background-color: transparent;
}
#controlsWrap > #buttonsContainer .controlButton:hover {
  color: var(--ui-text-strong);
}
#controlsWrap > #buttonsContainer .controlButton {
  /* icon styling */
}
#controlsWrap > #buttonsContainer .controlButton > svg {
  display: block;
  margin: auto;
}
#controlsWrap > #buttonsContainer .controlButton > svg > g.stroke {
  fill: none;
  stroke: var(--ui-icon-outline);
  stroke-width: 2;
  stroke-linejoin: round;
  opacity: 0.4;
}
#controlsWrap > #buttonsContainer .controlButton > svg > g.fill {
  fill: currentColor;
  stroke: none;
}
/* Home is the one button here that isn't self-explanatory as a bare icon
   (unlike orbit/fly/walk/AR/VR/settings) — give it a visible label instead
   of the fixed 34x34 icon-only square the rest of this toolbar uses. */
#controlsWrap > #buttonsContainer #homeButton {
  width: auto;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px 0 8px;
}
#controlsWrap > #buttonsContainer #homeButton > svg {
  margin: 0;
  flex-shrink: 0;
}
#controlsWrap > #buttonsContainer #homeButton > span {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}
/* Jump between points of interest (settings.json -> annotations[]). Reuses
   the toolbar's own ODE chevron (#odeArrowIcon, mirrored for "prev") rather
   than a generic arrow, same brand touch as the annotation tab. */
#controlsWrap > #buttonsContainer #poiNavLabel {
  min-width: 150px;
  padding: 0 6px;
  text-align: center;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
#controlsWrap > #buttonsContainer .controlButton {
  /* active highlight for simple toggle buttons (e.g. voxel overlay) */
}
#controlsWrap > #buttonsContainer .controlButton.active:not(.toggle) {
  color: var(--ui-accent);
}
#controlsWrap > #buttonsContainer .controlButton {
  /* camera toggle styling */
}
#controlsWrap > #buttonsContainer .controlButton.toggle {
  background: linear-gradient(90deg, transparent 0%, transparent 50%, var(--ui-accent) 50%, var(--ui-accent) 100%);
  background-size: 200% 100%;
  background-position: 100% 0%;
  background-repeat: no-repeat;
  transition: background-position 0.1s ease-in-out;
}
#controlsWrap > #buttonsContainer .controlButton.toggle.left {
  border-radius: 4px 0px 0px 4px;
}
#controlsWrap > #buttonsContainer .controlButton.toggle.left:not(.active) {
  background-position: 0% 0%;
}
#controlsWrap > #buttonsContainer .controlButton.toggle.middle {
  border-radius: 0;
}
#controlsWrap > #buttonsContainer .controlButton.toggle.middle:not(.active) {
  background-position: 0% 0%;
}
#controlsWrap > #buttonsContainer .controlButton.toggle.right {
  border-radius: 0px 4px 4px 0px;
}
#controlsWrap > #buttonsContainer .controlButton.toggle.right:not(.active) {
  background-position: 200% 0%;
}

/* settingsPanel */
#settingsPanel {
  position: fixed;
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + 70px);
  /* Higher than #minimap (11) and #annotationPanel (12) — both can occupy
     this exact bottom-right corner, and without this #minimap's z-index
     rendered on top, making an opened panel invisible (not just "behind the
     briefing modal", which only hid it while that was also open). */
  z-index: 21;
  padding: 12px;
  border-radius: 16px;
  border: 1px solid var(--ui-border);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  font-size: 14px;
  color: var(--ui-text);
  background-color: var(--ui-surface);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}
#settingsPanel > .settingsRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  width: 100%;
}
#settingsPanel > .settingsRow > button {
  flex-grow: 1;
  height: 34px;
  padding: 0 20px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8px;
  font-size: 13px;
  cursor: pointer;
  color: var(--ui-muted);
  background-color: rgba(255, 255, 255, 0.04);
  transition: background-color 250ms ease, color 250ms ease;
}
#settingsPanel > .settingsRow > button:hover {
  color: var(--ui-text-strong);
  background-color: rgba(255, 255, 255, 0.1);
}
#settingsPanel > .settingsRow > div {
  height: 34px;
  display: flex;
  align-items: center;
  padding: 0 8px;
  cursor: pointer;
  color: var(--ui-muted);
}
#settingsPanel > .settingsRow > div.toggleSwitch {
  padding: 0 4px;
}
#settingsPanel > .settingsRow > div.toggleSwitch .toggleTrack {
  width: 36px;
  height: 20px;
  border-radius: 10px;
  background-color: rgba(255, 255, 255, 0.15);
  position: relative;
  transition: background-color 200ms ease;
}
#settingsPanel > .settingsRow > div.toggleSwitch .toggleTrack .toggleThumb {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background-color: #777;
  position: absolute;
  top: 2px;
  left: 2px;
  transition: transform 200ms ease, background-color 200ms ease;
}
#settingsPanel > .settingsRow > div.toggleSwitch.active .toggleTrack {
  background-color: var(--ui-accent-soft);
}
#settingsPanel > .settingsRow > div.toggleSwitch.active .toggleTrack .toggleThumb {
  transform: translateX(16px);
  background-color: var(--ui-accent);
}
#settingsPanel > .settingsRow > div:hover {
  color: var(--ui-text);
}
#settingsPanel > .divider {
  width: 100%;
  height: 1px;
  margin: 8px 0;
  background-color: var(--ui-border);
}

/* infoPanel */
#infoPanel {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(2px);
}
#infoPanel > #infoPanelContent {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  min-height: 280px;
  min-width: 320px;
  padding: 8px;
  border-radius: 16px;
  border: 1px solid var(--ui-border);
  display: flex;
  flex-direction: column;
  color: var(--ui-text);
  background-color: var(--ui-surface);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}
#infoPanel > #infoPanelContent > #tabs {
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: 12px;
  background-color: var(--ui-surface-solid);
}
#infoPanel > #infoPanelContent > #tabs > .tab {
  padding: 8px 12px;
  border-radius: 10px;
  cursor: pointer;
  flex-grow: 1;
  text-align: center;
  font-weight: bold;
  font-size: 14px;
  color: var(--ui-muted);
  transition: background-color 250ms ease, color 250ms ease;
}
#infoPanel > #infoPanelContent > #tabs > .tab:hover {
  color: var(--ui-text-strong);
  background-color: rgba(255, 255, 255, 0.06);
}
#infoPanel > #infoPanelContent > #tabs > .tab.active {
  color: var(--ui-text-strong);
  background-color: rgba(255, 255, 255, 0.1);
}
#infoPanel > #infoPanelContent > #infoPanels {
  padding: 16px;
}
#infoPanel > #infoPanelContent > #infoPanels h1 {
  font-size: 11px;
  font-weight: bold;
  text-transform: uppercase;
  letter-spacing: 1px;
  padding: 4px 0 8px 0;
  color: var(--ui-accent);
}
#infoPanel > #infoPanelContent > #infoPanels .control-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 32px;
  padding: 5px 8px;
  margin: 0 -8px;
  border-radius: 6px;
  font-size: 13px;
}
#infoPanel > #infoPanelContent > #infoPanels .control-item > .control-action {
  text-align: left;
}
#infoPanel > #infoPanelContent > #infoPanels .control-item > .control-key {
  text-align: right;
  color: var(--ui-muted);
  font-size: 12px;
}
#infoPanel > #infoPanelContent > #infoPanels .control-item:nth-child(odd) {
  background-color: rgba(255, 255, 255, 0.03);
}
#infoPanel > #infoPanelContent > #infoPanels .control-spacer {
  margin: 14px 0;
}

#joystickBase {
  position: absolute;
  width: 56px;
  height: 100px;
  transform: translate(-50%, -50%);
  border-radius: 28px;
  touch-action: none;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.2666666667) 0%, rgba(0, 0, 0, 0) 30%, rgba(0, 0, 0, 0) 70%, rgba(0, 0, 0, 0.2666666667) 100%);
  background-color: rgba(0, 0, 0, 0.2);
  border: 2px solid rgba(255, 255, 255, 0.2);
  transition: width 0.2s ease, height 0.2s ease, border-radius 0.2s ease;
}
#joystickBase > #joystick {
  position: absolute;
  left: 8px;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  touch-action: none;
  background-color: rgba(255, 255, 255, 0.5333333333);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  transition: left 0.1s ease;
}
#joystickBase.mode-2d {
  width: 100px;
  height: 100px;
  border-radius: 50px;
  background: radial-gradient(circle, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, 0.2666666667) 100%);
  background-color: rgba(0, 0, 0, 0.2);
}
#joystickBase.mode-2d > #joystick {
  left: 30px;
}

#tooltip {
  display: none;
  position: absolute;
  border-radius: 4px;
  padding: 4px 4px;
  font-size: 12px;
  color: var(--ui-text);
  background-color: var(--ui-surface-solid);
}

#annotations {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: -1;
}

#annotations * {
  pointer-events: auto;
}

/* annotationPanel — the ODE-styled sidebar that replaced the old prev/next bar.
   Always present: a pull tab on its outer edge opens and closes it, and it lists
   every point of interest. Its colours are the --ann-* / --fp-* tokens on
   :root (top of this file), so a theme can restyle it without touching these
   rules. */
#annotationPanel {
  /* Also fed into the top inset and max-height below. */
  --panel-gap: 14px;
  /* How far the pull tab sits below the panel's top edge. A length, not a
     percentage — see #annotationTab. */
  --tab-top: 72px;
  position: absolute;
  /* Flush against the left edge (no inset there any more) but still clear
     of the outer page's header above. No `bottom` — the panel's height
     follows its content (floor plan + list) and stops right after the last
     point, rather than stretching all the way down with empty space below a
     short list. max-height keeps it from running past the viewport on a
     long list; #annotationPanelBody's own overflow-y then takes over. */
  top: var(--panel-gap);
  max-height: calc(100% - var(--panel-gap) * 2);
  left: 0;
  z-index: 12;
  width: min(380px, 86vw);
  display: flex;
  font-family: "Lato", Arial, sans-serif;
  color: var(--ann-text);
  /* Closed: parked just off the left edge, the tab still sticking out. */
  transform: translateX(-100%);
  transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
}
#annotationPanel.open {
  transform: none;
}
#annotationPanelInner {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background-color: var(--ann-surface);
  /* Rounded on the right only — the floating edge. The left edge sits flush
     against the viewport, so a rounded corner there would just cut a notch
     out of an otherwise solid edge. */
  border-radius: 0 var(--ann-panel-radius) var(--ann-panel-radius) 0;
  overflow: hidden;
  pointer-events: auto;
}

/* The pull tab, riding on the panel's outer edge. Opaque like the heading
   bars — it's a control, always on screen even when the panel is closed, so
   it needs to read against whatever the scene looks like there. */
/* Selector carries #annotationPanel so it outranks the generic
   `#annotationPanel button` reset further down — same for .annotationItem. */
#annotationPanel #annotationTab {
  position: absolute;
  left: 100%;
  /* A fixed distance below the panel's top edge, deliberately not a percentage
     of its height. The panel is content-sized (no `bottom`, height follows the
     floor plan + list), so a percentage re-placed the tab every time the
     content changed — expanding a point or the floor plan finishing loading
     slid it down the screen. An absolute offset keeps it put, and the upper
     area still clears the scene's focal point and the bottom control bar.
     min() only bites on a panel shorter than the offset plus the tab, where it
     stops the tab hanging past the bottom corner. */
  top: min(var(--tab-top), calc(100% - 60px));
  width: 30px;
  height: 60px;
  border: 1px solid var(--ann-border);
  border-left: 0;
  border-radius: 0 6px 6px 0;
  color: var(--ann-text);
  background-color: var(--ann-surface);
  pointer-events: auto;
}
#annotationPanel #annotationTab:hover {
  color: var(--ann-text);
  background-color: var(--ann-hover);
}
#annotationTab > svg {
  transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
}
#annotationPanel.open #annotationTab > svg {
  transform: rotate(180deg);
}

#annotationPanel button {
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 4px;
  color: var(--ann-muted);
  background-color: transparent;
}
#annotationPanel button:hover {
  color: var(--ann-text);
  background-color: var(--ann-button-hover);
}
#annotationPanel button:disabled {
  opacity: 0.3;
  cursor: default;
  background-color: transparent;
}
#annotationPanel svg {
  display: block;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The floor plan title and the language switch share this one row.
   #floorPlanHead sits at the start; #langToggle pushes itself to the end via
   its own margin-left rather than `justify-content: space-between`, so it
   stays pinned right even on scenes with no floor plan, where the title
   stays hidden and this row would otherwise have only one child. */
#annotationPanelHead {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px 8px 14px 16px;
  background-color: var(--ann-head-bg);
  border-bottom: 2px solid var(--ann-border);
}
#floorPlanHead {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ann-muted);
}
/* Overrides the generic #annotationPanel button's fixed 30x30 square (see
   below) — this one carries a text label next to the icon, not just a
   glyph. */
#annotationPanel #langToggle,
#annotationPanelHead > .langPick {
  margin-left: auto;
  flex-shrink: 0;
  width: auto;
  height: auto;
  gap: 5px;
  padding: 6px 9px;
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.04em;
}
#annotationPanelBody {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* the global `touch-action: none` would otherwise block scrolling on touch */
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
  -moz-user-select: text;
       user-select: text;
  -webkit-user-select: text;
}
/* touch-action isn't inherited, so the global `* { touch-action: none; }`
   reset re-applies to every element inside here too — a finger actually
   landing on the annotation text (or any other nested element) would still
   have its pan gesture blocked even though the scroll container above
   allows pan-y. Restated on every descendant so touch-scrolling works no
   matter what's directly under the finger. */
#annotationPanelBody * {
  touch-action: pan-y;
}
#annotationPanelBody::-webkit-scrollbar {
  width: 8px;
}
#annotationPanelBody::-webkit-scrollbar-thumb {
  border-radius: 4px;
  background-color: var(--ann-scrollbar);
}

/* floor plan */
#floorPlanStage {
  position: relative;
  /* Full panel width, with the height following the plan's own viewBox ratio
     (set from JS at load time). Deliberately no max-height: capping the height
     while the width is 100% breaks the ratio, and the drawing then letterboxes
     inside a too-wide box — which reads as the plan being wrongly aligned
     rather than as the box being clamped. A very tall plan makes a tall block
     here and the panel body just scrolls. */
  display: block;
  width: 100%;
  padding: 0;
  overflow: hidden;
  background-color: var(--ann-surface);
  border-bottom: 2px solid var(--ann-border);
}
#floorPlanImage,
#floorPlanOverlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
#floorPlanImage {
  object-fit: contain;
  -webkit-user-select: none;
     -moz-user-select: none;
          user-select: none;
}
/* The panel's generic `#annotationPanel svg` rule sets fill/stroke/stroke-width,
   and all three inherit — so every value the overlay relies on is restated here
   rather than left to cascade in from the icon styling. Scoped by #ui, not the
   panel, so it outranks that rule there (2-0-0 vs 1-0-1) and still applies
   when the plan has been moved out into #minimap. */
#ui #floorPlanOverlay {
  fill: none;
  stroke: none;
  stroke-width: 0;
  overflow: hidden;
}
/* Strokes are declared in CSS pixels and held there: the plan's viewBox units
   are arbitrary, so a plain stroke-width would come out hairline on one drawing
   and slab-thick on the next. */
#ui #floorPlanOverlay * {
  vector-effect: non-scaling-stroke;
}

/* "You are here": a dot for the position, a wedge for what the camera can see. */
#floorPlanCone {
  fill: var(--fp-marker);
  fill-opacity: 0.22;
  stroke: var(--fp-marker);
  stroke-width: 1;
  stroke-opacity: 0.5;
}
#floorPlanDot {
  fill: var(--fp-marker);
  stroke: var(--fp-marker-stroke);
  stroke-width: 2;
}

/* One pin per point of interest, numbered to match the list and the 3D hotspots. */
.floorPlanPin {
  cursor: pointer;
}
.floorPlanPin > circle {
  /* Black at rest, stepping to a lighter grey on hover/press (see below). */
  fill: var(--fp-pin);
  stroke: var(--fp-pin-stroke);
  stroke-width: 1.5;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.35));
}
.floorPlanPin > text {
  /* White reads on the now-darker circle; the old dark-grey-on-light-grey
     combination only worked when the fill was pale. */
  fill: var(--fp-pin-text);
  stroke: none;
  font-family: inherit;
  font-weight: 600;
  text-anchor: middle;
  dominant-baseline: central;
  /* the label is decoration on top of its own click target */
  pointer-events: none;
}
/* Lighter than --ann-muted on purpose — this is a one-off, not shared with
   the panel's muted text, so it's free to be lighter without dimming
   anything else. */
.floorPlanPin:hover > circle {
  fill: var(--fp-pin-active);
}
.floorPlanPin.active > circle {
  fill: var(--fp-pin-active);
}
.floorPlanPin.active > text {
  fill: var(--fp-pin-text);
}
.floorPlanPin:focus-visible {
  outline: none;
}
.floorPlanPin:focus-visible > circle {
  stroke: var(--fp-pin);
  stroke-width: 2.5;
}

/* list of points */
#annotationList {
  list-style: none;
  margin: 0;
  padding: 0;
  background-color: var(--ann-surface);
  /* Bottom-right only, matching #annotationPanelInner's top-right-only
     rounding: the left edge is flush against the viewport (square there),
     and the list's own background doesn't reach the panel's true bottom
     edge (empty space past a short list), so without this the last row
     would end in a hard square edge mid-panel instead of at a corner. */
  border-radius: 0 0 calc(var(--ann-panel-radius) - 4px) 0;
}
.annotationGroup {
  padding: 12px 16px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ann-muted);
  border-bottom: 1px solid var(--ann-hairline);
}
#annotationPanel .annotationItem {
  width: 100%;
  height: auto;
  gap: 12px;
  justify-content: flex-start;
  padding: 13px 16px;
  border-radius: 0;
  border-bottom: 1px solid var(--ann-hairline);
  font: inherit;
  font-size: 15px;
  text-align: left;
  color: var(--ann-text);
}
/* The last row's divider had nothing to divide from — it just cut across
   the list card's rounded bottom corners. */
#annotationList > li:last-child > .annotationItem {
  border-bottom: none;
}
#annotationPanel .annotationItem:hover {
  color: var(--ann-text);
}
#annotationPanel .annotationItem.active {
  box-shadow: inset 3px 0 0 var(--ann-accent);
  font-weight: 600;
  /* Its own divider would otherwise land between the title and the card
     that opens right below it — the wrong side. The divider belongs after
     the expanded content instead (see #annotationDetail.open below), right
     above whichever point comes next. */
  border-bottom: none;
}
.annotationDot {
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--ann-dot-text);
  background-color: var(--ann-dot-bg);
}
.annotationItem.active > .annotationDot {
  color: var(--ann-accent-text);
  background-color: var(--ann-accent);
}
.annotationItemTitle {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* selected point — expands inline under its list row (openDetailAt in
   index.js reparents this shared node there), not a separate view.
   #annotationDetail is the grid track the CSS grid-rows/fr trick animates
   between 0fr and 1fr; #annotationDetailInner is the overflow clip that
   trick needs; #annotationDetailCard is the actual card, kept as its own
   element so its margin/padding never fights the collapse (a
   margin on the clipped track element itself can leave a residual gap even
   at 0fr). */
#annotationDetail {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
}
#annotationDetail.open {
  grid-template-rows: 1fr;
}
/* The divider that used to sit under the active row's title moves here,
   after its expanded content — but only when there's a next point to divide
   it from; the last row already drops its own divider for the same reason
   (see the :last-child rule above). */
#annotationList > li:not(:last-child) #annotationDetail.open {
  border-bottom: 1px solid var(--ann-hairline);
}
#annotationDetailInner {
  overflow: hidden;
  min-height: 0;
}
#annotationDetailCard {
  /* Centers its content (the text, and the gallery/link when present)
     vertically instead of leaving it pinned to the top with dead space
     below whenever the card ends up taller than that content. */
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Top margin too now (was 0) — flush against the row above made the card
     look glued to it instead of sitting with its own breathing room. */
  margin: 16px;
  padding: 14px 16px 16px;
  border-radius: 10px;
  background-color: var(--ann-surface);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}
/* Already shown as the list row's own label — repeating it here would
   just duplicate it. */
#annotationTitle {
  display: none;
}

#annotationGallery {
  position: relative;
  margin: 14px 0 0;
  aspect-ratio: 16/10;
  border-radius: 4px;
  overflow: hidden;
  background-color: var(--ann-surface);
}
#annotationSlides {
  position: absolute;
  inset: 0;
}
#annotationSlides > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity 0.25s ease-out;
}
#annotationSlides > img.active {
  opacity: 1;
}
#annotationPanel .galleryNav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 44px;
  border-radius: 4px;
  color: var(--ann-text);
  background-color: rgba(255, 255, 255, 0.82);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
}
#annotationPanel .galleryNav:hover {
  background-color: #fff;
}
#annotationImagePrev {
  left: 6px;
}
#annotationImageNext {
  right: 6px;
}
#annotationDots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 8px;
  display: flex;
  justify-content: center;
  gap: 6px;
}
#annotationPanel #annotationDots > button {
  width: 8px;
  height: 8px;
  min-width: 8px;
  border-radius: 50%;
  background-color: rgba(255, 255, 255, 0.75);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
}
#annotationPanel #annotationDots > button.active {
  background-color: var(--ann-accent);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
}
#annotationCaption {
  margin: 8px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ann-muted);
}

/* Markdown body */
#annotationText {
  font-size: 14.5px;
  line-height: 1.6;
  color: var(--ann-body-text);
}
#annotationText:not(:empty) {
  margin-top: 12px;
}
#annotationText p {
  margin: 0 0 10px;
}
#annotationText > :last-child {
  margin-bottom: 0;
}
#annotationText h3,
#annotationText h4,
#annotationText h5 {
  margin: 16px 0 6px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ann-text);
}
#annotationText h3 {
  font-size: 16px;
}
#annotationText h4 {
  font-size: 14.5px;
}
#annotationText h5 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ann-muted);
}
#annotationText ul,
#annotationText ol {
  margin: 0 0 10px;
  padding-left: 20px;
}
#annotationText li {
  margin: 0 0 4px;
}
#annotationText blockquote {
  margin: 0 0 10px;
  padding: 2px 0 2px 12px;
  border-left: 2px solid var(--ann-line);
  color: var(--ann-muted);
}
#annotationText hr {
  height: 0;
  margin: 14px 0;
  border: 0;
  border-top: 1px solid var(--ann-line);
}
#annotationText code {
  padding: 1px 4px;
  border-radius: 3px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
  background-color: var(--ann-surface);
}
#annotationText a {
  color: var(--ann-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* external link button */
#annotationLink {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 18px;
  /* 1px less than the look calls for: the (default transparent) border adds it back */
  padding: 10px 13px;
  border-radius: 4px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  color: var(--ann-link-text);
  background-color: var(--ann-link-bg);
  border: 1px solid var(--ann-link-border);
}
#annotationLink:hover {
  color: var(--ann-link-hover-text);
  background-color: var(--ann-link-hover-bg);
}
#annotationLink > svg {
  flex-shrink: 0;
}

/* floor plan as a corner minimap (floorPlan.placement: "minimap") — the same
   #floorPlan section, moved here by initFloorPlan. Clear of the toolbar below
   it; the height follows the drawing's own aspect ratio, as in the panel. */
#minimap {
  position: absolute;
  right: max(16px, env(safe-area-inset-right));
  bottom: calc(max(16px, env(safe-area-inset-bottom)) + 62px);
  z-index: 11;
  width: min(420px, 52%);
  border: 1px solid var(--fp-minimap-border);
  border-radius: 8px;
  overflow: hidden;
  background-color: var(--fp-minimap-bg);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}
/* Show/hide button in the map's top-right corner. With the map collapsed the
   box shrinks to just this button — still anchored to the same corner as the
   map (right edge + whichever of top/bottom the placement uses), so the
   button doesn't jump. */
#minimapToggle {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--fp-minimap-border);
  border-radius: 50%;
  background-color: var(--fp-toggle-bg);
  color: var(--fp-toggle-text);
  cursor: pointer;
}
#minimapToggle:hover {
  background-color: var(--fp-toggle-hover-bg);
}
#minimapToggle > svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linejoin: round;
}
#minimap.collapsed {
  width: auto;
  padding: 6px;
  border-color: transparent;
  background-color: transparent;
  box-shadow: none;
  overflow: visible;
}
#minimap.collapsed > :not(#minimapToggle) {
  display: none;
}
#minimap.collapsed > #minimapToggle {
  position: static;
}
#minimap #floorPlanStage {
  border-bottom: 0;
  background-color: transparent;
}
#minimapCredit {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 7px 14px;
  border-top: 1px solid var(--fp-minimap-border);
  font-size: 9px;
  line-height: 1.4;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--fp-credit-text);
  background-color: var(--fp-credit-bg);
}
/* Wraps text + the reveal button so the pair shrinks/truncates as one unit,
   with the licence link (outside this wrapper) staying full-size beside it. */
#minimapCreditMain {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  flex: 1 1 auto;
}
#minimapCreditText {
  min-width: 0;
}
#minimapCreditInfo {
  display: none;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  opacity: 0.8;
  cursor: pointer;
}
#minimapCreditInfo:hover {
  opacity: 1;
}
#minimapLicense {
  flex: 0 0 auto;
  margin-left: auto;
  color: inherit;
  text-decoration: none;
}
#minimapLicense[href]:hover {
  color: var(--fp-credit-hover);
  text-decoration: underline;
}
/* Phones: a smaller map, and the credit text truncated to one line — there's
   no room for the full attribution beside the licence link otherwise. Tap
   #minimapCreditInfo to reveal it in full (the .expanded rules below) rather
   than just hiding part of what's often a licence-required attribution. */
@media (max-width: 640px) {
  #minimap:not(.collapsed) {
    width: min(260px, 44%);
  }
  #minimapCredit {
    padding: 5px 8px;
    font-size: 8px;
  }
  #minimapCreditText {
    display: inline-block;
    max-width: 90px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
  }
  #minimapCreditInfo {
    display: flex;
  }
  #minimapCredit.expanded {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
  #minimapCredit.expanded #minimapCreditText {
    max-width: none;
    white-space: normal;
    overflow: visible;
  }
  /* margin-left: auto pins the licence to the row's far end in the default
     single-line layout — in the expanded column it would instead shove it
     off to the right of its own line for no reason. */
  #minimapCredit.expanded #minimapLicense {
    margin-left: 0;
  }
}

@media (max-width: 480px) {
  #annotationPanel {
    width: min(320px, 86vw);
  }
}

/* Project briefing modal (settings.json -> briefing) — bilingual onboarding
   overlay, built once by initFloorPlan's sibling initBriefing. Stays entirely
   .hidden (never built) on a scene without a `briefing` key, so every rule
   here is dormant by default. */
#briefingModal {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background-color: var(--brief-overlay-bg);
}
#briefingCard {
  display: flex;
  flex-direction: column;
  width: min(680px, 100%);
  max-height: 100%;
  min-height: 0;
  background-color: var(--brief-bg);
  color: var(--brief-text);
  border: 1px solid var(--brief-border);
  border-radius: var(--brief-radius);
  overflow: hidden;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}
#briefingHead {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 20px 160px 20px 26px;
  background-color: var(--brief-head-bg);
}
#briefingTitle {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--brief-head-text);
}
#briefingSubtitle {
  font-family: 'Lato', Arial, sans-serif;
  font-size: 17px;
  font-weight: 300;
  color: var(--brief-accent);
}
#briefingHeadActions {
  position: absolute;
  top: 16px;
  right: 18px;
  display: flex;
  align-items: center;
  gap: 10px;
}
/* Same markup/shape as the sidebar's #langToggle (icon + label, no border/
   box) — see index.html's comment there for why it's a second, separate
   element. Colour direction is deliberately its own: this one sits in the
   modal's solid dark header strip, not the sidebar's panel, so it reads
   white by default and dims to grey on hover rather than the other way
   round. */
#briefingLangToggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: auto;
  height: auto;
  padding: 5px 7px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--brief-head-text);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
}
#briefingLangToggle:hover,
#briefingLangToggle[aria-expanded="true"] {
  color: var(--brief-muted);
  background-color: rgba(0, 0, 0, 0.3);
}

/* Three or more languages: initUI wraps each language button in .langPick
   and hangs its own menu (.langMenuList) under it — not a native <select>,
   whose popup the OS draws and CSS can't reach. Colours are --lang-menu-*
   tokens (see :root), solid on purpose: the panel's own surface can be
   transparent in a theme. */
.langPick {
  position: relative;
  display: flex;
  flex-shrink: 0;
  z-index: 30;
}
.langMenuList {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  /* as wide as the longest name, no wider */
  width: max-content;
  display: flex;
  flex-direction: column;
  padding: 3px;
  background-color: var(--lang-menu-bg);
  border: 1px solid var(--lang-menu-border);
  border-radius: 6px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}
/* Restated under both parents: #annotationPanel button (the generic 30x30
   reset) and the briefing header's button rules outrank a bare class. */
.langMenuList > button,
#annotationPanel .langMenuList > button {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  width: 100%;
  height: auto;
  padding: 5px 10px 5px 8px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--lang-menu-text);
  font: inherit;
  font-size: 13px;
  font-weight: 400;
  letter-spacing: normal;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.langMenuList > button:hover,
.langMenuList > button:focus-visible,
#annotationPanel .langMenuList > button:hover {
  color: var(--lang-menu-text);
  background-color: var(--lang-menu-hover);
  outline: none;
}
/* the button stays highlighted while its menu is open */
#annotationPanel #langToggle[aria-expanded="true"] {
  color: var(--ann-text);
  background-color: var(--ann-button-hover);
}
.langMenuList > button[aria-checked="true"] {
  font-weight: 700;
}
.langMenuList > button[aria-checked="true"]::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--lang-menu-accent);
}
.langMenuList > button[aria-checked="false"]::before {
  content: '';
  width: 6px;
}
.langMenu::after {
  content: '';
  margin-left: 1px;
  border: 4px solid transparent;
  border-bottom: 0;
  border-top-color: currentColor;
}
#briefingSettings,
#briefingClose {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--brief-head-text);
  cursor: pointer;
}
#briefingSettings:hover,
#briefingClose:hover {
  background-color: rgba(0, 0, 0, 0.3);
}
#briefingBody {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* the global `touch-action: none` would otherwise block touch scrolling —
     same fix as #annotationPanelBody, restated on every descendant because
     touch-action isn't inherited */
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 22px 26px;
}
#briefingBody * {
  touch-action: pan-y;
}
#briefingBody p {
  margin: 0;
  font-size: 14px;
  line-height: 1.6;
  color: var(--brief-body-text);
}
/* A blank line in the Markdown is a new paragraph and has to read as one —
   with margin 0 alone, two paragraphs in a plain (non-flex) text block touch
   and look like a single line break. */
#briefingBody p + p {
  margin-top: 8px;
}
/* …except where the column is a flex box whose 8px gap already does it */
#briefingBody .briefSectionRow > .briefSectionText > p + p {
  margin-top: 0;
}
#briefingBody a {
  color: var(--brief-text);
  text-decoration: underline;
}
#briefingBody a:hover {
  color: var(--brief-muted);
}
.briefSection {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--brief-border);
}
.briefSection h3 {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--brief-text);
}
.briefSectionRow {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
.briefSectionRow > .briefSectionText {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.briefSectionRow > img {
  flex: 0 0 auto;
  width: 104px;
  height: auto;
  border-radius: 4px;
  align-self: center;
}
/* optional per-section timeline (briefing.sections[].timeline): one row per
   step, date column then text, with a dot per step and a line joining them.
   The rows are subgrids of one list grid, so the date column is as wide as
   the longest date in every row and the dates line up. The line runs from
   below each dot through the row gap to the next dot, whatever the text's
   height; the last row has none. (Without subgrid support a row falls back
   to one column: date above text.) */
.briefTimeline {
  list-style: none;
  margin: 4px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  row-gap: 12px;
}
.briefTimeline > li {
  position: relative;
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  column-gap: 10px;
  padding-left: 16px;
  font-size: 14px;
  line-height: 1.5;
}
/* 8px dot centred on the first line (1.5 line-height => centre at 0.75em) */
.briefTimeline > li::before {
  content: '';
  position: absolute;
  left: 0;
  top: calc(0.75em - 4px);
  box-sizing: border-box;
  width: 8px;
  height: 8px;
  border: 1.5px solid var(--brief-text);
  border-radius: 50%;
  background-color: var(--brief-accent);
}
.briefTimeline > li:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 3px;
  top: calc(0.75em + 6px);
  /* through the 12px row gap, stopping 2px short of the next dot */
  bottom: calc(-12px - 0.75em + 6px);
  width: 2px;
  background-color: var(--brief-border);
}
/* Steps with an `icon`: a 16px icon takes the dot's place. Once any step has
   one, every row gets the wider 16px slot (so the dates still line up) and a
   plain dot sits centred in it, with the line on that same centre. */
.briefTimeline.hasIcons > li {
  padding-left: 24px;
}
.briefTimeline.hasIcons > li::before {
  left: 4px;
}
.briefTimeline > li.hasIcon::before {
  content: none;
}
.briefTimeline.hasIcons > li:not(:last-child)::after {
  left: 7px;
  top: calc(0.75em + 10px);
  bottom: calc(-12px - 0.75em + 10px);
}
.briefTimelineIcon {
  position: absolute;
  left: 0;
  top: calc(0.75em - 8px);
  fill: none;
  stroke: var(--brief-text);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.briefTimelineDate {
  font-weight: 700;
  white-space: nowrap;
  color: var(--brief-text);
}
.briefTimelineText {
  color: var(--brief-body-text);
}
/* optional per-section buttons (briefing.sections[].link, one or an array).
   Scoped under #briefingBody so they outrank the plain `#briefingBody a`
   text-link rule. The first is solid, any further ones outlined. */
.briefLinks {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}
/* .briefLink is also used on a <button> — the "#terms" sentinel link (see
   initBriefing) opens the terms modal in-app rather than navigating, which
   is a button semantically, but needs to look identical to a real link. */
#briefingBody a.briefLink,
#briefingBody button.briefLink {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  /* 1px less than the look calls for: the border adds it back */
  padding: 7px 13px;
  border: 1px solid var(--brief-cta-bg);
  border-radius: 4px;
  background-color: var(--brief-cta-bg);
  color: var(--brief-cta-text);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-decoration: none;
  cursor: pointer;
}
#briefingBody a.briefLink:hover,
#briefingBody button.briefLink:hover {
  border-color: var(--brief-cta-hover-bg);
  background-color: var(--brief-cta-hover-bg);
  color: var(--brief-cta-text);
}
#briefingBody a.briefLink.secondary,
#briefingBody button.briefLink.secondary {
  background-color: transparent;
  color: var(--brief-text);
}
#briefingBody a.briefLink.secondary:hover,
#briefingBody button.briefLink.secondary:hover {
  border-color: var(--brief-cta-bg);
  background-color: var(--brief-cta-bg);
  color: var(--brief-cta-text);
}
/* optional small print under a section (briefing.sections[].note) */
.briefNote {
  font-size: 12px;
  line-height: 1.5;
  color: var(--brief-muted);
}
/* #briefingBody p (body text, 14px) would otherwise outrank .briefNote and
   render the small print at body size */
#briefingBody .briefNote p {
  margin: 0;
  font-size: inherit;
  line-height: inherit;
  color: inherit;
  /* small print stays left-aligned and unhyphenated (see the justified
     running-text rule further down) */
  text-align: inherit;
  -webkit-hyphens: manual;
  hyphens: manual;
}
#briefingBody .briefNote a {
  color: inherit;
}
.briefLink > svg {
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.briefKeys {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 18px 0 6px;
  padding: 14px 18px;
  width: min(380px, 100%);
  align-self: center;
  background-color: var(--brief-kbd-bg);
  border-radius: 4px;
}
.briefKeyRow {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-size: 13px;
  color: var(--brief-kbd-text);
}
.briefKeyRow > .briefKeyAction {
  flex: 1 1 auto;
}
.briefKeyRow > .briefKeyCombo {
  flex: 0 0 auto;
  font: 12px/1.4 ui-monospace, Menlo, Consolas, monospace;
}
#briefingFoot {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 26px;
  border-top: 1px solid var(--brief-border);
}
#briefingCta {
  order: 2;
  margin-left: auto;
  flex: 0 0 auto;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 20px;
  border: 0;
  border-radius: 4px;
  background-color: var(--brief-cta-bg);
  color: var(--brief-cta-text);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
#briefingCta:hover {
  background-color: var(--brief-cta-hover-bg);
}
#briefingFootnote {
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--brief-muted);
}
/* Narrow screens: image beside text leaves the text a ~190px column next to
   the 104px picture (on a 360px phone), so stack the section instead —
   heading, then the text at full width, then the picture centred at a modest
   size below it. Body/foot padding shrinks too, which is width the text gets back. */
@media (max-width: 560px) {
  #briefingHead {
    padding-left: 18px;
  }
  #briefingBody {
    gap: 12px;
    padding: 18px 18px 22px;
  }
  .briefSectionRow {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  .briefSectionRow > img {
    align-self: center;
    width: auto;
    max-width: 120px;
    max-height: 88px;
    object-fit: contain;
  }
  /* image.placement "body": dissolve the text column into the stacked row so
     the picture can slot between the body text and the timeline. */
  .briefSectionRow.imageAfterBody > .briefSectionText {
    display: contents;
  }
  .briefSectionRow.imageAfterBody > img {
    order: 1;
  }
  .briefSectionRow.imageAfterBody > .briefSectionText > .briefTimeline {
    order: 2;
  }
  .briefKeys {
    padding: 12px 14px;
  }
  /* no room for a date column: date above its text */
  .briefTimeline {
    grid-template-columns: 1fr;
  }
  .briefTimeline > li {
    grid-template-columns: 1fr;
  }
  .briefKeyRow {
    gap: 10px;
  }
  #briefingFoot {
    flex-wrap: wrap;
    padding: 14px 18px calc(14px + env(safe-area-inset-bottom));
  }
  #briefingCta {
    flex: 1 1 100%;
    justify-content: center;
    margin-left: 0;
  }
}
@media (max-width: 480px) {
  #briefingModal {
    padding: 0;
  }
  #briefingCard {
    width: 100%;
    height: 100%;
    max-height: none;
    border-radius: 0;
  }
  /* The actions row no longer floats over the title here (see below), so
     #briefingHead doesn't need to reserve space for it on every line —
     title/subtitle get the card's full width instead of wrapping into a
     ~60%-wide column while the icons sit mostly empty space of their own. */
  /* The 36px buttons already carry ~8px of empty box above their icons, so
     the head's own top padding is mostly redundant here. */
  #briefingHead {
    padding-top: 6px;
    padding-right: 18px;
  }
  #briefingHeadActions {
    position: static;
    order: -1;
    margin-top: 0;
    margin-bottom: 4px;
    justify-content: flex-end;
    gap: 6px;
  }
}

/* Terms/conditions overlay (settings.json -> briefing.terms) — a second,
   independent modal opened from a section link's "#terms" sentinel (see
   initBriefing), reusing the same --brief-* tokens as the briefing modal so
   the two read as one family. Simpler than
   that one: just a title/close head and a scrollable body, no footer. */
#termsModal {
  position: absolute;
  inset: 0;
  z-index: 21;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background-color: var(--brief-overlay-bg);
}
#termsCard {
  display: flex;
  flex-direction: column;
  width: min(560px, 100%);
  max-height: 100%;
  min-height: 0;
  background-color: var(--brief-bg);
  color: var(--brief-text);
  border: 1px solid var(--brief-border);
  border-radius: var(--brief-radius);
  overflow: hidden;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}
#termsHead {
  position: relative;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 18px 60px 18px 22px;
  background-color: var(--brief-head-bg);
}
#termsTitle {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--brief-head-text);
}
#termsClose {
  position: absolute;
  top: 10px;
  right: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: var(--brief-head-text);
  cursor: pointer;
}
#termsClose:hover {
  background-color: rgba(0, 0, 0, 0.3);
}
#termsBody {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  touch-action: pan-y;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px 22px;
}
#termsBody * {
  touch-action: pan-y;
}
#termsBody p {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: var(--brief-body-text);
}
#termsBody h3,
#termsBody h4,
#termsBody h5 {
  margin: 4px 0 0;
  font-weight: 700;
  line-height: 1.3;
  color: var(--brief-text);
}
#termsBody h3 {
  font-size: 14px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
#termsBody ul,
#termsBody ol {
  margin: 0;
  padding-left: 20px;
}
#termsBody li {
  margin: 0 0 4px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--brief-body-text);
}
/* Running text (briefing and terms paragraphs, annotation bodies) is
   justified. hyphens: auto keeps the gaps between words even in the narrow
   columns; it follows <html lang>, which initUI keeps in step with the DE/EN
   switch, so words break by the right language's rules. Lines ending in a
   forced break (single newline in the Markdown) stay left-aligned, so
   address blocks and the like are unaffected. Short items — headings, lists,
   the timeline, small print — keep the default alignment. */
#briefingBody p,
#termsBody p,
#annotationText p {
  text-align: justify;
  -webkit-hyphens: auto;
  hyphens: auto;
}
#termsBody a {
  color: var(--brief-text);
  text-decoration: underline;
}
#termsBody a:hover {
  color: var(--brief-muted);
}
@media (max-width: 480px) {
  #termsModal {
    padding: 0;
  }
  #termsCard {
    width: 100%;
    height: 100%;
    max-height: none;
    border-radius: 0;
  }
}

/*# sourceMappingURL=index.css.map */