@charset "UTF-8";


* {
	font-family: var(--font-base);
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}

html, body {
	width: 100%;
	height: 100%;
	font-size: 100%;
	overflow: hidden;
}

body {
	background-color: var(--color-bg-page);
	color: var(--color-text);
	font-size: var(--font-size-sm);
	font-weight: normal;
}

h1, h2, h3 {
	margin: 0 0 var(--spacing-sm) 0;
}

img {
	border: 0;
}

button, input, select {
	font-family: inherit;
	font-size: var(--font-size-md);
}

.hidden {
	display: none !important;
}

#banner {
	flex: 0 0 auto;
	height: 100px;
	/* ROOT-RELATIVE, not //scstatehouse.gov/... . The absolute form pins the image to one bare host:
	   a page served from www, or from a test copy on any other name, then fetches it cross-origin
	   from wherever that host happens to point \u2014 which is how it came to load on one player and not
	   the next, purely on what each browser had cached. This resolves against whoever served the
	   page, which is the only host guaranteed to have the file sitting beside it. */
	background: url('/video/images/bannerarchive2.jpg') no-repeat center center;
	background-size: cover;
	border-bottom: 1px solid var(--color-border);
	position: relative;
}

#bannerinfo {
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 54px;
	color: var(--color-text-inverse);
	text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
}
#bannerinfo span {
	font-family: 'Great Vibes', cursive;
}
.banner-medium,
.banner-short { display: none; }

.content {
	flex: 1 1 0;
	display: flex;
	gap: var(--content-gap);
	padding: var(--spacing-base);
	min-height: 0;
	overflow: hidden;
	width: 100%;
	max-width: var(--content-max-width);
	margin: 0 auto;
}

#group1 {
	flex: 1 1 calc(var(--viewer-basis) * 1%);
	max-width: var(--viewer-max-width);
	display: flex;
	flex-direction: column;
	min-height: 0;
	overflow: hidden;
}

#group2 {
	flex: 0 0 calc(var(--meeting-list-basis) * 1%);
	max-width: var(--meeting-list-max-width);
	display: flex;
	flex-direction: column;
	min-height: 0;
	overflow: hidden;
}

#videobox {
	flex: 0 1 auto;
	min-height: 0;
	aspect-ratio: 16 / 9;
	width: 100%;
	max-width: 1280px;
	align-self: center;
	overflow: hidden;
	background: black;
	border: 1px solid var(--color-border);
	border-top: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Base colour only — the archive standby poster is resolved from the REST standby manifest and
   written as an inline backgroundImage by TranscriptPlayer.showStandby (deferred until REST
   resolves it). No static image reference lives in CSS. */
/* `.covered` is set by setMessage for exactly as long as a message is showing — the load has not
   finished, or it failed. The standby image is already painted on this element; these rules are what
   make it visible, so the two states share them rather than keeping two copies that could drift. */
#videobox.standby .widescreen,
#videobox.covered .widescreen {
	background-color: var(--color-brand);
	background-repeat: no-repeat;
	background-position: top center;
	background-size: 100% auto;
}
/* The embed itself, hidden while there is nothing in it worth seeing. An unloaded player paints plain
   black over the poster otherwise. visibility rather than display, so the frame is not torn out of the
   layout and reloaded the moment it comes back. */
#videobox.covered #mediaspace { visibility: hidden; }

.relative {
	position: relative;
}

.widescreen {
	aspect-ratio: 16 / 9;
	height: 100%;
	width: auto;
	max-width: 100%;
}

#mediaspace {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	border: 0;
	overflow: hidden;
}

#message {
	position: absolute;
	bottom: var(--spacing-xl);
	left: 0;
	right: 0;
	color: var(--color-text-inverse);
	font-size: var(--font-size-overlay);
	text-align: center;
	text-shadow: 1px 1px 2px var(--color-border);
	transition: opacity 20ms linear;   /* fast fade on message change; keep in sync with MESSAGE_FADE_MS */
	pointer-events: none;   /* status overlay: never intercept clicks on the player beneath, even at opacity:0 */
}

#tabcontrols {
	flex: 0 0 auto;
	padding: var(--spacing-md);
	background-color: var(--color-bg-controls);
	border: 1px solid var(--color-border);
	border-top: 0;
}

#peaks-zone {
	flex: 0 0 auto;
	position: relative;
	height: var(--peaks-height);
	padding: 0;
	background: var(--peaks-bg);
	border: 1px solid var(--color-border);
	border-top: 0;
	cursor: ew-resize;
	user-select: none;
	display: none;
}
#peaks-zone.visible { display: block; }

#peaks-canvas {
	display: block;
	width: 100%;
	height: 100%;
	border-radius: var(--radius-base);
}

#peaks-segments {
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.peaks-segment {
	position: absolute;
	top: 2px;
	height: 10px;
	border: 1px solid rgba(0, 0, 0, 0.35);
	border-radius: var(--radius-sm);
	pointer-events: auto;
	cursor: pointer;
	transition: filter 0.15s;
}
.peaks-segment:hover { filter: brightness(1.25); }

.segment-label {
	position: absolute;
	left: 4px;
	right: 4px;
	top: 50%;
	transform: translateY(-50%);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	font-family: var(--font-base);
	font-size: var(--font-size-xs);
	pointer-events: none;
	color: rgba(0, 0, 0, 0.8);
	line-height: 1;
}

.marker-line {
	position: absolute;
	top: 0;
	height: 100%;
	width: 2px;
	transform: translateX(-50%);
	pointer-events: auto;
	cursor: pointer;
	background: rgba(255, 255, 255, 0.7);
	transition: background 0.15s;
}
.marker-line:hover { background: #fff; }
.marker-line.marker-active { background: #fff; }

.marker-label {
	position: absolute;
	top: 3px;
	left: 50%;
	transform: translateX(-50%);
	white-space: nowrap;
	font-family: var(--font-base);
	font-size: var(--font-size-xs);
	color: #fff;
	background: rgba(0, 0, 0, 0.65);
	padding: 1px 4px;
	border-radius: var(--radius-sm);
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.15s;
}
.marker-line:hover .marker-label,
.marker-line.marker-active .marker-label { opacity: 1; }

.label-editor {
	position: fixed;
	background: rgba(0, 0, 0, 0.88);
	border: 1.5px solid rgba(255, 255, 255, 0.85);
	border-radius: var(--radius-base);
	padding: 3px 7px;
	z-index: 9999;
	transform: translate(-50%, -100%);
}
.label-editor::after {
	content: '';
	position: absolute;
	bottom: -8px;
	left: 50%;
	transform: translateX(-50%);
	border: 7px solid transparent;
	border-top-color: rgba(255, 255, 255, 0.85);
	border-bottom: none;
}
.label-editor-input {
	background: transparent;
	border: none;
	outline: none;
	color: #fff;
	font-family: var(--font-base);
	font-size: var(--font-size-xs);
	min-width: 80px;
	max-width: 220px;
}

.controls-row {
	display: flex;
	align-items: center;
	gap: var(--spacing-lg);
	flex-wrap: nowrap;
	width: 100%;
}

.btn-group {
	display: flex;
	flex: 0 0 auto;
}

.btn-group .tab {
	padding: var(--spacing-xs) var(--spacing-md);
	border: 1px solid var(--color-tab-border);
	background: var(--color-tab-bg);
	cursor: pointer;
	font-weight: normal;
	font-size: var(--font-size-sm);
	margin: 0;
}

/* Tabs follow the icon+label button paradigm: glyph plus text on desktop and tablet, collapsing to
   glyph-only at the phone breakpoint (see max-width: 600px), where six chamber tabs won't fit as words. */
.btn-group .tab .tab-label { margin-left: 0.4em; }

.btn-group .tab:first-child {
	border-radius: var(--radius-base) 0 0 var(--radius-base);
}

.btn-group .tab:not(:first-child) {
	border-left: none;
}

.btn-group .tab:last-child {
	border-radius: 0 var(--radius-base) var(--radius-base) 0;
}

.btn-group .tab:hover:not(:disabled):not(.active) {
	background: var(--color-tab-hover);
}

/* unify selection + focus: focused tabs use the same brand blue as the selected tab,
   so keyboard nav previews selection and selected tabs don't get clobbered by hover/focus gray */
.btn-group .tab.active,
.btn-group .tab:focus-visible:not(:disabled) {
	background: var(--color-brand);
	color: var(--color-text-inverse);
	border-color: var(--color-brand-dark);
	outline: none;
}

.btn-group .tab.active {
	font-weight: 600;
}

.btn-group .tab:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

.part-selector select {
	padding: var(--spacing-xs) var(--spacing-md);
	border-radius: var(--radius-base);
	border: 1px solid var(--color-border-light);
}

.action-bar {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--spacing-sm);
	padding: var(--spacing-md) var(--spacing-xl);
	background-color: var(--color-bg-controls);
	border-top: 1px solid var(--color-border);
}

.action-bar label {
	font-size: var(--font-size-base);
	color: var(--color-text-secondary);
}

.action-bar input {
	padding: var(--spacing-xs) var(--spacing-md);
	font-size: var(--font-size-base);
	border: 1px solid var(--color-border-light);
	border-radius: var(--radius-base);
}

.action-bar select {
	padding: var(--spacing-xs) var(--spacing-md);
	font-size: var(--font-size-base);
	border: 1px solid var(--color-border-light);
	border-radius: var(--radius-base);
}

.action-bar button {
	padding: var(--spacing-xs) var(--spacing-md);
	font-size: var(--font-size-base);
	background-color: var(--color-bg-content);
	border: 1px solid var(--color-border-light);
	border-radius: var(--radius-base);
	cursor: pointer;
}

.action-bar button:disabled {
	opacity: 0.5;
	cursor: not-allowed;
}

.action-bar button:not(:disabled):hover {
	background-color: var(--color-bg-hover);
}

#panel-transcript .action-bar input { max-width: 200px; }

.search-count {
	font-size: var(--font-size-sm);
	color: var(--color-text-muted);
	display: inline-block;
	min-width: 3.5em;
	text-align: center;
}

.bar-actions {
	position: absolute;
	right: var(--spacing-md);
	top: 50%;
	transform: translateY(-50%);
	display: flex;
	align-items: center;
	gap: var(--spacing-md);
}

.bar .part-selector {
	display: none;
	align-items: center;
	gap: var(--spacing-sm);
}
.bar .part-selector.visible { display: flex; }
.bar .part-selector label {
	font-size: var(--font-size-base);
}
.bar .part-selector select {
	padding: var(--spacing-xs) var(--spacing-md);
	border-radius: var(--radius-base);
	border: 1px solid var(--color-tab-hover);
	font-size: var(--font-size-base);
}

.share-btn {
	padding: var(--spacing-xs) var(--spacing-md);
	background: var(--color-bg-controls);
	color: var(--color-text);
	border: 1px solid var(--color-tab-hover);
	border-radius: var(--radius-base);
	font-size: var(--font-size-base);
	cursor: pointer;
	display: none;
}
.bar .share-btn.visible { display: block; }

.share-btn:hover {
	background: var(--color-tab-hover);
}

.share-btn.copied {
	background: var(--color-success);
	border-color: var(--color-success-dark);
}
/* Glyph + text buttons (share / download / trim / search / clear): the label sits after the icon on
   desktop and is hidden on mobile, leaving an icon-only control. The download button reuses .share-btn
   styling but is an <a>, so drop the link underline. */
.btn-label { margin-left: 0.4em; }
.download-btn { text-decoration: none; }

#content {
	flex: 1 1 0;
	overflow: hidden;
	border: 1px solid var(--color-border);
	border-top: 0;
	border-radius: 0 0 var(--radius-lg) var(--radius-lg);
	min-height: 220px;
	display: flex;
	flex-direction: column;
}

.panel-content {
	flex: 1 1 0;
	overflow-y: auto;
	min-height: 0;
}

.panel-content.hidden {
	display: none;
}

#panel-transcript {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

/* The background belongs to the SCROLLING SURFACE, not to what happens to be in it.
   It used to sit on .transcript-document, a class that is added when a document renders and removed
   when there is none — so with no transcript nothing painted the panel at all, and the only colour
   came from the message's own box, which is the width and height of its text. */
#transcript-content {
	flex: 1 1 0;
	overflow-y: auto;
	min-height: 0;
	background-color: var(--color-bg-content);
}

/* Chamber document tabs (Calendar/Amendment/Bill/Journal): the panel fills the content pane and its
   iframe fills the panel, mirroring #panel-transcript / #transcript-content. */
#panel-calendar, #panel-amendment, #panel-bill, #panel-journal, #panel-agenda {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}
.doc-frame {
	flex: 1 1 0;
	width: 100%;
	border: 0;
	min-height: 0;
	background-color: var(--color-bg-content);   /* white content bg, matching the help/info panels */
}

/* Paragraph transcript ------------------------------------------------------------------------
   The composite transcript renders as a two-column reading view, so the whole .transcript-row /
   .transcript-text block from video.css is replaced here. Markup contract:

     #transcript-content                 the scroller
       .transcript-controls              sticky: viewer options + speaker colour key
       .transcript-document              re-rendered per meeting/part
         .transcript-paragraph           one entry
           .transcript-speaker-track     LEFT column; filled only where the speaker changes
           .transcript-body              RIGHT column; the prose
             span.tw                     one word; carries data-start / data-end
               span.search-match         highlight fragment (one match may span several .tw)

   Word spans include their own leading space (as a bare text node before .tw-ink), so the
   paragraph's text is character-identical to the source text. That is what lets a phrase match
   highlight continuously across word boundaries — do not move the space out of the span. */

.transcript-controls {
	position: sticky;
	top: 0;
	z-index: 2;
	display: flex;
	flex-direction: column;
	gap: var(--spacing-xs);
	padding: var(--spacing-md) var(--spacing-lg);
	background-color: var(--color-bg-content);
	border-bottom: 1px solid var(--color-border-subtle);
}

.transcript-options {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-lg);
	align-items: center;
}

.transcript-option {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-xs);
	font-size: var(--font-size-sm);
	color: var(--color-text-secondary);
	cursor: pointer;
	user-select: none;
}

/* accent-color paints the checked box with the theme's brand blue instead of the browser default,
   which is a different blue on every platform. */
.transcript-option input {
	cursor: pointer;
	margin: 0;
	accent-color: var(--color-brand);
}
.transcript-option:hover { color: var(--color-brand); }

/* Sits in the options row and is pushed to the far right. Matches the bar's clip-download control
   (same glyph, same "Download" label, same chrome) but carries its own class: .share-btn is
   display:none until a `.bar .share-btn.visible` rule reveals it, which would never fire here. */
.transcript-download {
	margin-left: auto;
	display: inline-flex;
	align-items: center;
	padding: var(--spacing-xs) var(--spacing-md);
	background: var(--color-bg-controls);
	color: var(--color-text);
	border: 1px solid var(--color-tab-hover);
	border-radius: var(--radius-base);
	font-size: var(--font-size-base);
	cursor: pointer;
	white-space: nowrap;
}

.transcript-download:hover { background: var(--color-tab-hover); }

/* Colour key: one swatch per voice in the document, in order of first appearance. */
/* The key is a legend, not content. Dragging across the transcript to select a passage catches it on
   the way past and puts speaker names into the copied text, which is not what anyone selecting the
   document means to take. The prose below stays selectable; this does not. */
.transcript-key {
	display: flex;
	flex-wrap: wrap;
	gap: var(--spacing-md);
	align-items: center;
	font-size: var(--font-size-xs);
	color: var(--color-text-secondary);
	user-select: none;
	-webkit-user-select: none;
}

.speaker-swatch {
	display: inline-flex;
	align-items: center;
	gap: var(--spacing-xs);
	color: var(--speaker-color);
	font-weight: 600;
}

.speaker-dot {
	width: 9px;
	height: 9px;
	border-radius: 50%;
	background-color: var(--speaker-color);
	display: inline-block;
	flex: 0 0 auto;
}

/* Said plainly rather than left for the reader to infer from a key of one. */
.speaker-caveat {
	color: var(--color-text-muted);
	font-style: italic;
}

/* No background: #transcript-content, which this always sits inside, paints the surface for both the
   loaded and the empty state. Repeating it here would be a second place to change it and a second
   place to forget. */
.transcript-document {
	padding: var(--spacing-lg);
	line-height: var(--line-height-relaxed);
}

/* Two columns. With the speaker track switched off the grid collapses to one and the prose takes
   the freed width — the more readable layout when there is nothing to attribute. */
.transcript-paragraph {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: var(--spacing-lg);
	padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm) var(--spacing-md);
	border-left: 3px solid transparent;
	border-radius: var(--radius-base);
	transition: background-color 160ms ease;
}

/* Uncurated documents carry only a timecode in the track, so the column is narrowed to fit one;
   curated documents need room for a name above it. */
.transcript-document.show-speakers .transcript-paragraph {
	grid-template-columns: var(--transcript-time-track) minmax(0, 1fr);
}

.transcript-document.show-speakers.curated .transcript-paragraph {
	grid-template-columns: var(--transcript-speaker-track) minmax(0, 1fr);
}

.transcript-speaker-track { display: none; }

.transcript-document.show-speakers .transcript-speaker-track {
	display: flex;
	flex-direction: column;
	gap: 1px;
	padding-top: 2px;
	min-width: 0;
}

/* A rule down the left edge carries the voice colour on every paragraph, including the ones whose
   track is deliberately blank, so a long stretch from one speaker still reads as one stretch.
   Curated only — an uncurated document sets no --speaker-color, and referencing an undefined
   custom property here would make the declaration invalid at computed-value time. */
.transcript-document.show-speakers.curated .transcript-paragraph { border-left-color: var(--speaker-color); }

.transcript-speaker {
	font-family: var(--font-base);
	font-size: var(--font-size-xs);
	font-weight: 700;
	line-height: 1.25;
	color: var(--speaker-color);
	overflow-wrap: anywhere;
}

.transcript-time {
	font-family: var(--font-mono);
	font-size: var(--font-size-sm);
	color: var(--color-text-muted);
	white-space: nowrap;
	cursor: pointer;
	align-self: start;
}

/* text-decoration-color is stated explicitly rather than left to currentColor, so the underline
   names the theme blue instead of inheriting whatever the text happens to be. */
.transcript-time:hover {
	color: var(--color-brand);
	text-decoration: underline;
	text-decoration-color: var(--color-brand);
}

.transcript-body {
	margin: 0;
	font-family: var(--font-content);
	font-size: var(--font-size-md);
	line-height: var(--line-height-relaxed);
	color: var(--color-text);
	cursor: pointer;
}

/* With no speaker track the prose gets the whole panel — it flows to whatever width is actually
   available. No max-width here: a synthetic measure cap looks like a rendering fault, since the
   panel is already sized by the page layout and the reader chose this mode to get the space. */
.transcript-document:not(.show-speakers) .transcript-body {
	font-size: var(--font-size-lg);
}

/* One word. Inline so it wraps naturally; the leading space lives inside the span, so the hover
   and active tints extend a touch to the left of the glyphs by design. */
/* Every word carries its leading inter-word space as a bare text node, with the printable text in a
   child .tw-ink. All highlights (hover, playhead, search) paint .tw-ink only, so a space is never
   highlighted even though it stays inside the word span for search alignment. */
.tw-ink { border-radius: var(--radius-sm); }
.tw:hover .tw-ink { background-color: var(--color-brand-light); }

/* The playhead classes are maintained whatever the options say; these two rules decide whether
   either one paints, so toggling a checkbox costs one class flip and never a re-render. */
.transcript-document.track-paragraphs .transcript-paragraph.active-paragraph {
	background-color: var(--transcript-active-paragraph);
}

/* Highlights are background-colour ONLY — no outline, box-shadow, border, font-weight or font-size.
   These marks move with the playhead across thousands of words, so anything that changes a word's
   painted extent makes the line appear to shift under the reader. Colour alone leaves the glyphs
   exactly where they were. */
.transcript-document.track-words .tw.active-word .tw-ink {
	background-color: var(--transcript-active-word);
}

/* Words inside a region the transcriber flagged as dropout / inaudible / garbled carry .tw-cue and
   a tooltip naming the marker.

   DELIBERATELY UNSTYLED. This previously drew a pink line under the word (a gradient at 85% height)
   — indistinguishable from a browser spell checker, and on a long recording it covers hundreds of
   words, reading as the page being corrected rather than as a fact about the audio. A treatment
   that does not imitate a spellchecker (a margin glyph or a background tint) can be switched on
   here later; --transcript-cue-mark is kept in the theme for that.

   Correction provenance is NOT here to style. Which words a pass rewrote, and what they said
   before, is editorial working state that only the editor renders — the player never emits it. */

/* Paragraphs the transcriber flagged as overlapping speech read as less certain. */
.transcript-paragraph.overlapped .transcript-body { font-style: italic; }

/* Same rule as the playhead highlights: colour only. Match and current-match stay distinguishable
   by hue (pale blue vs amber) rather than by an outline weight. */
.transcript-body .search-match {
	background-color: var(--color-highlight-search);
	border-radius: var(--radius-sm);
}

.transcript-body .search-match.current-match {
	background-color: var(--color-highlight-current);
}

/* No background and no min-height here: the surface behind it is painted and full-height already.
   The min-height was never doing anything anyway — a percentage resolves against a parent with a
   definite height, and this one is sized by its content. */
.no-content {
	padding: var(--spacing-xl);
	text-align: center;
	color: var(--color-text-muted);
}

.info-panel {
	display: flex;
	flex-direction: column;
	width: 100%;
	/* cap to the container width so the quick-links block can't grow the panel; combined with
	   .links-row's max-width:100% this lets a long committee name wrap instead of widening it */
	max-width: 100%;
	/* fill the container in 2-column desktop (the parent #panel-info has a definite height there);
	   the mobile breakpoint resets this to content size where the parent height is auto */
	height: 100%;
	background-color: var(--color-bg-content);
}

.quick-links {
	flex: 1 1 auto;
	display: flex;
	align-items: center;
	padding: var(--spacing-xl);
	background-color: var(--color-bg-content);
	position: relative;
	/* size container so the seal underlay can hide itself at a static breakpoint. inline-size (not
	   size) because size containment collapses this flex item on the mobile auto-height panel. */
	container-type: inline-size;
}
/* Grayscale, muted chamber-seal underlay. It paints only where a player sets --quick-links-seal
   (the chamber player, via live.js); elsewhere the var is unset and this resolves to no image. The
   seal is centered and contain-scaled so it never clips, and the @container rule below hides it when
   the quick-links box is too narrow for the seal to read. */
.quick-links::before {
	content: '';
	position: absolute;
	/* 2.5% inset all around keeps the contain-scaled seal ~5% off full, so it never kisses the edges */
	inset: 2.5%;
	z-index: 0;
	background-image: var(--quick-links-seal, none);
	background-position: center;
	background-repeat: no-repeat;
	background-size: contain;
	filter: grayscale(1);
	opacity: 0.1;
	pointer-events: none;
}
/* Hide the seal once the quick-links box is narrower than this static breakpoint (tune to taste).
   Keyed off the box's inline size, so it reflects the actual display area, not the viewport. */
@container (max-width: 200px) {
	.quick-links::before {
		display: none;
	}
}
.quick-links-content {
	flex: 1 1 0;
	/* sit above the seal underlay */
	position: relative;
	z-index: 1;
	/* a flex item defaults to min-width:auto, which refuses to shrink below its content's
	   intrinsic width — so .links-row's max-content (a long committee name) forces this wide and
	   overflows. min-width:0 lets it shrink so .links-row's max-width:100% caps it and names wrap. */
	min-width: 0;
}

.quick-links h2 {
	font-size: var(--font-size-lg);
	margin-bottom: var(--spacing-base);
	text-align: center;
	color: var(--color-brand);
}

.links-row {
	display: grid;
	grid-template-columns: 1fr 1fr;
	column-gap: var(--spacing-xl);
	width: max-content;
	max-width: 100%;
	margin: 0 auto;
}

.links-column {
	min-width: 0;
}

.links-column ul {
	list-style: disc;
	margin: 0 0 0 var(--spacing-lg);
	padding: 0;
}

.links-column li {
	margin-bottom: var(--spacing-sm);
	text-align: left;
}

a[data-click] {
	/* These links carry no href (which would echo into the status bar on hover), so the browser
	   won't give them an anchor's interactive cursor — restore it for every data-click link at once. */
	cursor: pointer;
}
.links-column a {
	color: var(--color-link);
	text-decoration: none;
	font-size: var(--font-size-base);
	/* let a long committee name wrap within its column rather than forcing the block wider */
	overflow-wrap: break-word;
}

.links-column a:hover {
	text-decoration: underline;
	color: var(--color-link-hover);
}
/* RSS/podcast feed row: a "<Chamber> RSS Feeds" label followed by the video + audio feed icons. Each
   icon is a real feed hyperlink — left-click views it (XSLT), right-click copies the URL. */
.rss-feeds .rss-link {
	margin-left: var(--spacing-sm);
	font-size: var(--font-size-base);
}
.rss-feeds .rss-link:hover {
	text-decoration: none;
	color: var(--color-link-hover);
}

.bill-results {
	display: block;
	flex: 1 1 0;
	width: 100%;
	min-height: 0;
	border: 0;
	margin: 0;
	overflow-y: auto;
}

#panel-info form {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--spacing-md);
	flex-wrap: wrap;
}

#panel-info form input[type="text"] {
	font-family: Courier, monospace;
}

#panel-info form button { padding: var(--spacing-xs) 12px; }

#panel-info form .form-row {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm);
}
#panel-info form .form-buttons {
	display: flex;
	align-items: center;
	gap: var(--spacing-sm);
}

.help-content {
	padding: var(--spacing-xl);
	line-height: var(--line-height-relaxed);
	background-color: var(--color-bg-content);
}

.help-section {
	margin-bottom: 25px;
	padding-bottom: var(--spacing-xl);
	border-bottom: 1px solid var(--color-border-subtle);
}

.help-section:last-child {
	margin-bottom: 0;
	padding-bottom: 0;
	border-bottom: none;
}

.help-section h3 {
	color: var(--color-brand);
	margin-bottom: var(--spacing-base);
	font-size: var(--font-size-lg);
}

.help-section p {
	font-size: var(--font-size-base);
	margin-bottom: var(--spacing-base);
	color: var(--color-text-secondary);
}

.help-section ul {
	margin: var(--spacing-base) 0 0 var(--spacing-xl);
	font-size: var(--font-size-base);
	color: var(--color-text-secondary);
}

.help-section li {
	margin-bottom: 6px;
}

.help-tables {
	display: flex;
	gap: var(--spacing-xl);
	align-items: flex-start;
	flex-wrap: wrap;
	margin: var(--spacing-base) 0;
}
.help-table {
	width: 100%;
	max-width: 400px;
	border-collapse: collapse;
	margin: var(--spacing-base) 0;
	font-size: var(--font-size-base);
}
.help-tables .help-table {
	flex: 1;
	width: auto;
	max-width: none;
	min-width: 220px;
	margin: 0;
}

.help-table th,
.help-table td {
	padding: var(--spacing-md) 12px;
	text-align: left;
	border: 1px solid var(--color-border-subtle);
}

.help-table th {
	background-color: var(--color-bg-subtle);
	font-weight: 600;
	color: var(--color-text-secondary);
}

.help-table td:first-child {
	width: 100px;
	text-align: center;
}

.help-section kbd {
	display: inline-block;
	vertical-align: middle;
	padding: 3px var(--spacing-md);
	font-family: var(--font-mono);
	font-size: var(--font-size-sm);
	background-color: #f7f7f7;
	border: 1px solid var(--color-border-light);
	border-radius: var(--radius-base);
	box-shadow: 0 1px 0 #aaa;
}

.bar {
	position: relative;
	display: flex;
	align-items: center;
	text-align: center;
	background-color: var(--color-brand);
	color: var(--color-text-inverse);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
/* The seals are lifted out of flow (absolute) for centering, so they no longer stretch the bar. When
   a chamber seal is showing, grow the bar to contain the fixed 75px seal; with no seal shown the bar
   keeps its natural (content) height. Scoped above the 600px breakpoint that hides the seals entirely,
   so the bar collapses to content height on mobile rather than reserving seal space that isn't shown. */
@media (min-width: 601px) {
	.bar:has(#meetingseals:not(.hidden)) {
		min-height: calc(75px + 2 * var(--spacing-sm));
	}
}

.meeting-seals {
	position: absolute;
	left: var(--spacing-md);
	top: 50%;
	transform: translateY(-50%);
	display: flex;
	flex-direction: row;
	align-items: center;
}

.chamber-seal {
	width: 75px;
	height: 75px;
	margin: var(--spacing-sm);
}

/* Meeting info is centered across the whole bar regardless of the seal/action widths: it claims a
   full-width flex basis (like the meeting-list .title-bar) while its siblings are lifted out of flow
   (position: absolute) so they can't shrink or offset it. */
#meetinginfo {
	flex: 1 1 100%;
	padding: var(--spacing-sm);
}

#meetinginfo h2 {
	font-size: var(--font-size-lg);
	margin-bottom: var(--spacing-sm);
	text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
}

#meetinginfo h3 {
	font-size: var(--font-size-sm);
	font-weight: normal;
	text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
}
#databox {
	flex: 1 1 0;
	text-align: left;
	background-color: var(--color-bg-content);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	overflow: hidden;
}

#meeting-list {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

.list-toggle {
	padding: var(--spacing-xs) var(--spacing-md);
	background: var(--color-bg-controls);
	color: var(--color-text);
	border: 1px solid var(--color-tab-hover);
	border-radius: var(--radius-base);
	font-size: var(--font-size-base);
	cursor: pointer;
	display: none;
}
.list-toggle:hover { background: var(--color-tab-hover); }
.list-toggle.active {
	background: var(--color-brand);
	color: var(--color-text-inverse);
	border-color: var(--color-brand-dark);
}

/* trim-toggle: single atomic control — toggles the peaks zone AND the trim button panel together.
   Visible in every tier except when its parent (#tabcontrols) is hidden (e.g., fake-fullscreen). */
.trim-toggle {
	margin-left: auto;
	padding: var(--spacing-xs) var(--spacing-md);
	border: 1px solid var(--color-tab-border);
	background: var(--color-tab-bg);
	color: var(--color-text);
	border-radius: var(--radius-base);
	cursor: pointer;
	font-size: var(--font-size-sm);
}
.trim-toggle:hover { background: var(--color-tab-hover); }
.trim-toggle[aria-expanded="true"] {
	background: var(--color-brand);
	color: var(--color-text-inverse);
	border-color: var(--color-brand-dark);
}

/* trim-panel: in-flow section between peaks-zone and tabcontrols.
   Tape-player metaphor — transport row (7 buttons centered) + edit/history row (6 buttons centered).
   Dark palette mirrors the peaks zone to signal these are special trim-related controls. */
#trim-panel {
	flex: 0 0 auto;
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--spacing-xs);
	padding: var(--spacing-md);
	background: var(--peaks-bg);
	border: 1px solid var(--color-border);
	border-top: 0;
}
#trim-panel.hidden { display: none; }
.trim-row {
	display: flex;
	justify-content: center;
	gap: var(--spacing-xs);
	flex: 0 1 auto;
}
/* ARCHIVE: hairline rule between the three inline groups of #trim-panel — marking, transport,
   and download. A direct child of the panel, so it separates the .trim-row blocks themselves
   rather than anything inside them. Purely visual: aria-hidden, and no part in button sizing. */
.trim-divider {
	flex: 0 0 1px;
	align-self: stretch;
	margin: 0 var(--spacing-xs);
	background: rgba(255, 255, 255, 0.25);
}
/* icon-only buttons — sized to fit 7 across at 360px viewport.
   Slightly-darker-than-panel background marks these as a control-cluster within the dark zone. */
#trim-panel button {
	flex: 0 1 52px;
	min-width: 36px;
	height: 26px;
	padding: 0;
	font-size: 13px;
	background: #2a2a2a;
	color: var(--color-text-inverse);
	border: 1px solid #1a1a1a;
	border-radius: var(--radius-base);
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
/* The download button carries a text label, so it opts out of the fixed icon-only sizing and grows to
   fit its content (icon + label) with padding. */
#trim-panel button.download-segments {
	flex: 0 1 auto;
	width: auto;
	min-width: 0;
	padding: 0 var(--spacing-md);
	gap: 0.4em;
}
#trim-panel button:hover:not(:disabled) { background: #444; }
#trim-panel button:active:not(:disabled) { background: var(--color-brand); border-color: var(--color-brand-dark); }
#trim-panel button:disabled {
	opacity: 0.35;
	cursor: not-allowed;
}

@media (min-width: 1024px) and (max-height: 1180px) {
	#banner,
	.bar-footer { display: none; }
}

/* --breakpoint-stack: tablet-portrait and below */
@media only screen and (max-width: 1023px) {
	html, body { overflow: auto; }
	/* mobile: glyph-only action buttons — hide the text labels, keep the icons */
	.btn-label { display: none; }
	#banner { height: 60px; }
	#bannerinfo { font-size: 36px; }
	.banner-full { display: none; }
	.banner-medium { display: inline; }
	.content {
		flex-direction: column;
		padding: var(--spacing-sm);
		max-width: 100%;
		overflow: visible;
	}
	#group1 {
		flex: 0 0 auto;
		max-width: 100%;
		overflow: visible;
	}
	#group2 {
		/* full-viewport overlay above the page, toggled by .visible. position:fixed lifts it out of
		   the stacked column; z-index sits under the label-editor modal (9999). Shown when no video
		   is selected, on a filter/sort quick link, or via the List button; hidden when one is picked. */
		position: fixed;
		inset: 0;
		z-index: 1000;
		max-width: 100%;
		display: none;
		overflow: hidden;
		background: var(--color-bg-content);
	}
	#group2.visible { display: flex; }
	/* let transcript zone size to its content; body scroll handles overflow */
	#content {
		flex: 0 0 auto;
		min-height: 0;
		overflow: visible;
	}
	.panel-content {
		flex: 0 0 auto;
		overflow: visible;
	}
	/* single-column: info block sizes to content (it fills its container only in 2-column desktop) */
	.info-panel { height: auto; }
	.bill-results {
		flex: 0 0 auto;
		min-height: 100px;
		overflow: hidden;
	}
	#panel-transcript { overflow: visible; }
	#transcript-content {
		flex: 0 0 auto;
		overflow: visible;
	}
	#message { font-size: clamp(14px, 2.5vw, 22px); }
	.controls-row { flex-wrap: wrap; }
	.list-toggle { display: block; }
	/* archives aren't usable on small screens — hide the tab entirely rather than show a disabled control */
	.btn-group .tab[data-mode="transcript"] { display: none; }
	/* the agenda tab follows the same mobile rule: hidden on small screens rather than shown disabled */
	.btn-group .tab[data-mode="agenda"] { display: none; }
}

/* --breakpoint-phone: phone-portrait additions */
@media only screen and (max-width: 600px) {
	#banner,
	.bar-footer { display: none; }
	/* Phone: collapse tabs to glyph-only so all of them (six on the chamber player) fit the narrow row. */
	.btn-group .tab .tab-label { display: none; }
	#videobox, #tabcontrols, #content {
		border-left: 0;
		border-right: 0;
	}
	.content { padding: 0; }
	/* condense meeting bar: chamber seals consume too much width, location is secondary */
	#meetingseals { display: none !important; }
	.meeting-location { display: none; }
	.bar { min-height: 0; }
	#meetinginfo { padding: var(--spacing-sm); }
	#meetinginfo h3 { font-size: var(--font-size-xs); }
	/* quick links: collapse to one content-sized column */
	.links-row {
		grid-template-columns: 1fr;
		gap: 0;
	}
	/* bill search: one field per row, label inline with input, buttons own row */
	#panel-info form {
		flex-direction: column;
		align-items: stretch;
	}
	#panel-info form .form-row {
		width: 100%;
	}
	#panel-info form .form-row label {
		flex: 0 0 auto;
		min-width: 100px;
	}
	#panel-info form .form-row select,
	#panel-info form .form-row input[type="text"] {
		flex: 1 1 auto;
		width: auto;
	}
	#panel-info form .form-buttons {
		justify-content: center;
		width: 100%;
	}
}

/* touch-input device sizing — phones AND tablets */
@media (pointer: coarse) and (max-width: 1023px) {
	.btn-group .tab,
	.list-toggle,
	.share-btn,
	.trim-toggle,
	.bar .part-selector select,
	.action-bar button,
	.action-bar input,
	.action-bar select {
		min-height: var(--touch-control-height);
		font-size: var(--font-size-base);
		padding-top: 0;
		padding-bottom: 0;
	}
	.btn-group .tab { padding-left: 18px; padding-right: 18px; }
	/* form fields keep 16px to prevent iOS Safari auto-zoom on focus — buttons don't trigger zoom */
	.bar .part-selector select,
	.action-bar input,
	.action-bar select,
	#documentsearch,
	#bill-numbers,
	#bill-session { font-size: 16px; }
}

/* phone-landscape: CSS-only fake fullscreen (no chrome, video fills viewport) */
@media (pointer: coarse) and (max-height: 500px) and (orientation: landscape) {
	/* size to the DYNAMIC viewport, not 100%/100vh: on iOS those resolve to the large viewport (URL
	   bar retracted), so the video's bottom hides behind the still-visible URL bar. 100dvh fits the
	   currently-visible area instead, eliminating the clip. (iOS can't hide the bar for a CSS
	   fullscreen — only the player's native video-fullscreen does that.) */
	html, body { overflow: hidden; height: 100dvh; }
	#banner,
	.bar,
	#peaks-zone,
	#tabcontrols,
	#content,
	#group2,
	#trim-panel { display: none; }
	#group2.visible { display: none; } /* keep the list overlay closed in fake-fullscreen */
	.content {
		padding: 0;
		gap: 0;
		max-width: 100%;
	}
	#group1 {
		flex: 1 1 0;
		max-width: 100%;
		/* center the contained #videobox; its letterbox bars (left/right) fall in #group1 */
		align-items: center;
		background: #000;
	}
	/* Size the OUTER container to a contained 16:9 box so the WHOLE group — the .relative.widescreen
	   block, its absolutely-positioned overlays, the player iframe, and the standby poster background
	   — shares ONE box and stays aligned, instead of sizing each piece separately (which misaligns
	   overlays vs. background the moment #videobox != .widescreen). Height-constrained because
	   landscape is wider than 16:9; max-width guards the rare narrower case. .widescreen and
	   #videobox.standby then fill it through their base rules — no per-element overrides. */
	#videobox {
		flex: 0 0 auto;
		height: 100%;
		width: auto;
		max-width: 100%;
		aspect-ratio: 16 / 9;
		border: 0;
	}
}

@media print {
	#banner,
	.bar,
	#videobox,
	#peaks-zone,
	#tabcontrols,
	#group2,
	.action-bar { display: none !important; }

	html, body {
		display: block !important;
		overflow: visible;
		height: auto;
	}

	.content {
		display: block;
		max-width: 100%;
		padding: 0;
	}

	#group1 {
		display: block;
		max-width: 100%;
		overflow: visible;
	}

	#content {
		display: block;
		overflow: visible;
		border: 0;
		min-height: 0;
	}

	.panel-content {
		overflow: visible;
		height: auto;
	}

	.panel-content:not(.hidden) {
		display: block;
		width: 100%;
	}

	#panel-transcript {
		display: block;
		overflow: visible;
		height: auto;
	}

	#transcript-content {
		display: block;
		overflow: visible;
		height: auto;
	}
}

/* Every paragraph gets a little air above it, so the transcript reads as separated blocks rather
   than one wall of text. Applies whether or not the speaker track is showing, and whether or not the
   paragraph opens a new voice. Scoped to .transcript-document, so no other element is touched.

   NOTE: this block is duplicated verbatim in video.css and transcripts.css. Those two pages load
   no stylesheet in common — each is a full clone — so there is no single "global" file that reaches
   both. Change one, change the other. */
.transcript-document .transcript-paragraph:not(:first-child) {
	margin-top: var(--spacing-md);
}
