@font-face {
    font-family: "Archivo";
    src: url("/static/fonts/Archivo-Regular.ff1d543ccd.woff2") format("woff2"),
         url("/static/fonts/Archivo-Regular.22ec5eb680.woff") format("woff");
    font-weight: normal;
    font-style: normal;
    font-display: swap;
}

:root {
    --font: "Archivo", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --bg: #f4f6f8;
    --surface: #ffffff;
    --text: #1f2933;
    --muted: #616e7c;
    --border: #d8dee4;
    --accent: #1a4f8a;
    --accent-soft: #eaf1f9;
    --highlight: #fff3bf;
    --radius: 6px;
    --page-max: 1440px;
    --gutter: 0.75rem;
}

body {
    margin: 0;
    background-color: var(--bg);
    color: var(--text);
    font-family: var(--font);
    line-height: 1.6;
    /* Committee names and URLs are long enough to push a phone sideways. */
    overflow-wrap: break-word;
    -webkit-text-size-adjust: 100%;
}

/* Pure sets its own font stack on grids and form controls; keep them on the
   site font. */
.pure-g, .pure-g [class*="pure-u"], button, input, select, textarea {
    font-family: var(--font);
}

img, video, canvas {
    max-width: 100%;
}

h1, h2, h3, h4 {
    line-height: 1.25;
}

a {
    color: var(--accent);
}

.muted {
    color: var(--muted);
}

/* --- Page shell ------------------------------------------------------- */

.site-bar {
    background-color: var(--surface);
    border-bottom: 1px solid var(--border);
}

/* Brand left, links right. Pure's horizontal menu is inline-block by default. */
.site-bar .pure-menu-horizontal {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    max-width: var(--page-max);
    margin: 0 auto;
    padding: 0 1.5rem;
}

.site-bar .pure-menu-heading {
    color: var(--text);
    font-size: 1.05rem;
    text-transform: none;
}

/* The mobile menu is the checkbox and nothing else — see the narrow-screen
   block at the end of the file for what checking it does. Clipped rather
   than hidden so it keeps its place in the tab order. */
.nav-toggle {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
}

.nav-toggle-btn {
    display: none;
}

.page {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: 1.5rem;
}

.narrow {
    max-width: 34rem;
}

/* Pure's grid units carry no gutters, so opt into them per grid. */
.gutters {
    margin: 0 calc(-1 * var(--gutter));
}

/* Pure's units size in percentages without border-box, so the gutter padding
   has to be pulled inside the declared width or every column overflows by it. */
.gutters > [class*="pure-u"] {
    box-sizing: border-box;
    padding: 0 var(--gutter);
}

/* Sidebar column follows the reader down on wide screens. Pure's .pure-g is a
   flexbox, so a column stretches to the full height of the row by default and
   sticky has nothing to travel over -- align-self is what gives it its own
   content height back. Harmless where .sticky-col sits on a non-flex child. */
@media screen and (min-width: 64em) {
    .sticky-col {
        position: sticky;
        top: 1.5rem;
        align-self: flex-start;
        max-height: calc(100vh - 3rem);
        overflow-y: auto;
    }
}

footer.page {
    color: var(--muted);
    font-size: 0.9rem;
    border-top: 1px solid var(--border);
    margin-top: 2rem;
}

/* --- Legal pages (terms, privacy) ------------------------------------ */

.legal h2 {
    font-size: 1.2rem;
    margin-top: 2rem;
}

.legal h3 {
    font-size: 1rem;
}

.legal address {
    font-style: normal;
}

/* --- Surfaces --------------------------------------------------------- */

.panel {
    background-color: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.25rem;
    margin-bottom: 1.5rem;
}

/* Headings and paragraphs shouldn't push against the edges they sit next to. */
.page > :first-child,
.panel > :first-child {
    margin-top: 0;
}

.panel > :last-child {
    margin-bottom: 0;
}

.notice {
    padding: 1rem 1.25rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background-color: var(--surface);
}

.notice-error {
    border-color: #dcabab;
    background-color: #fdf3f3;
    color: #8a2626;
}

.notice-success {
    border-color: #a8cfad;
    background-color: #f2faf3;
    color: #1f5c2a;
}

/* --- Pricing -------------------------------------------------------- */

/* One card, wherever the price is shown: components.renderPricing. */
.pricing-card {
    background-color: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.25rem;
    max-width: 32rem;
}

.pricing-card > :first-child {
    margin-top: 0;
}

.pricing-card > :last-child {
    margin-bottom: 0;
}

/* The Pro tag on the Chat nav link, for the signed out. */
.nav-pro {
    display: inline-block;
    margin-left: 0.15rem;
    padding: 0 0.45rem;
    border-radius: 999px;
    background-color: var(--accent);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1.5;
    vertical-align: middle;
}

/* The account page's plan facts and buttons. */
.account-facts {
    margin: 0 0 1rem;
}

.account-facts dt {
    color: var(--muted);
    font-size: 0.85rem;
}

.account-facts dd {
    margin: 0 0 0.75rem;
}

.account-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* --- Tables ----------------------------------------------------------- */

.pure-table {
    width: 100%;
}

/* Wide tables scroll inside their panel rather than stretching the page.
   The min-width keeps the columns readable instead of crushing them on
   narrow screens, which is what makes the scroll worth having. */
.table-scroll {
    overflow-x: auto;
}

.table-scroll > table {
    min-width: 32rem;
}

/* A search in flight: Pure fades disabled buttons to near-invisible, so keep
   this one legible and put a spinner ahead of the label instead. htmx adds
   .htmx-request to a form's hx-indicator (the search button) while it waits. */
.pure-button.is-loading,
.pure-button.htmx-request {
    opacity: 0.85;
    cursor: progress;
}

.pure-button.is-loading::before,
.pure-button.htmx-request::before {
    content: "";
    display: inline-block;
    width: 0.9em;
    height: 0.9em;
    margin-right: 0.5em;
    vertical-align: -0.1em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: button-spin 0.7s linear infinite;
}

@keyframes button-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .pure-button.is-loading::before,
    .pure-button.htmx-request::before,
    .spinner {
        animation-duration: 2s;
    }
}

/* A standalone spinner, the button one grown up: used by the report modal. */
.spinner {
    width: 2.25rem;
    height: 2.25rem;
    margin: 0 auto 0.9rem;
    border: 3px solid var(--accent);
    border-right-color: transparent;
    border-radius: 50%;
    animation: button-spin 0.8s linear infinite;
}

.spinner[hidden] {
    display: none;
}

/* The "don't leave" modal shown while a report is generated. */
.report-dialog {
    width: min(26rem, calc(100vw - 2rem));
    padding: 1.5rem 1.5rem 1.25rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background-color: var(--surface);
    color: var(--text);
    text-align: center;
    box-shadow: 0 12px 40px rgba(31, 41, 51, 0.25);
}

.report-dialog::backdrop {
    background-color: rgba(31, 41, 51, 0.45);
}

.report-dialog h2 {
    font-size: 1.15rem;
    margin: 0 0 0.5rem;
}

.report-dialog p {
    margin: 0.5rem 0 0;
}

.report-dialog form {
    margin-top: 1rem;
}

/* Sortable headers are real buttons, drawn as plain header text. */
th a,
th button {
    all: unset;
    display: block;
    width: 100%;
    cursor: pointer;
    white-space: nowrap;
}

th a:hover,
th button:hover {
    text-decoration: underline;
}

#results-table-body tr {
    cursor: pointer;
    position: relative;
}

/* The title's link covers its row, so the whole row is clickable without a
   script, and it is still one real link for keyboards and new tabs. */
#results-table-body .row-link::after {
    content: "";
    position: absolute;
    inset: 0;
}

/* Pure's striping colours the cells, so the hover state has to as well. */
#results-table-body tr:hover td {
    background-color: var(--accent-soft);
}

/* Bill numbers off the meeting agenda, in the results title cell.
   A block-level flex container, not inline-flex: it has to claim its own line
   so the snippet below starts on the next one rather than running on after the
   last pill. Wrapping is what keeps a forty-bill agenda folding instead of
   pushing the table sideways. */
.pill-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin: 0.15rem 0 0.3rem;
}

/* --surface rather than --accent-soft on purpose: the row hover state *is*
   --accent-soft, and a pill painted with it would vanish under the cursor. */
.pill {
    display: inline-block;
    padding: 0 0.45rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background-color: var(--surface);
    color: var(--accent);
    font-size: 0.78rem;
    line-height: 1.6;
    white-space: nowrap;
}

/* The same yellow .matched-chunk uses on the hearing page, for the same
   reason: this is the thing you searched for. */
.pill-match {
    background-color: var(--highlight);
    border-color: #e0c96a;
    color: var(--text);
    font-weight: 600;
}

.pill-more {
    color: var(--muted);
}

/* --- Hearing page ----------------------------------------------------- */

/* Key/value metadata, two columns instead of stacked <dt>/<dd>. */
.meta-list {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.35rem 1.25rem;
    margin: 0;
}

.meta-list > div {
    display: contents;
}

.meta-list dt {
    color: var(--muted);
}

.meta-list dd {
    margin: 0;
}

summary {
    cursor: pointer;
    font-weight: 600;
}

/* Transcripts keep their source line breaks but still wrap. The line breaks
   are reinstated by app.chunkText -- chunk storage does not keep them -- and
   pre-wrap is what renders them. */
.transcript {
    margin: 0;
    font-family: var(--font);
    font-size: 0.8125rem;
    line-height: 1.7;
    white-space: pre-wrap;
    overflow-wrap: break-word;
}

/* One span per chunk, so a search result can be scrolled to by index. Purely
   structural: the transcript must read as continuous prose. */
.transcript-chunk {
    display: inline;
}

/* The passage a search result matched. scroll-margin keeps it clear of the
   sticky header when scrollToChunk brings it into view. */
.matched-chunk,
.transcript-chunk:target {
    background-color: var(--highlight);
    scroll-margin-top: 1rem;
}

video {
    width: 100%;
    height: auto;
    background-color: #000;
    border-radius: var(--radius);
}

/* --- Trends chart ----------------------------------------------------- */

/* Chart.js runs with maintainAspectRatio off, so the canvas takes its size
   from this box: tall enough to read on a phone, capped on a desktop. */
.chart-box {
    position: relative;
    height: 55vh;
    min-height: 260px;
}

figure {
    margin: 0;
}

/* --- Chat page -------------------------------------------------------- */

/* For labels a screen reader needs and the layout does not. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* The sidebar is a column whose list scrolls, so the report button stays at
   the bottom of it however many chats there are. On wide screens it takes the
   viewport's height, which is what makes the button omnipresent. */
.chat-sidebar {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

@media screen and (min-width: 64em) {
    .chat-sidebar {
        height: calc(100vh - 3rem);
        overflow: hidden;
    }

    .chat-list-toggle {
        flex: 1;
        min-height: 0;
        overflow-y: auto;
    }
}

.chat-list {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
}

.chat-list li {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0.35rem 0.4rem;
    border-radius: var(--radius);
}

.chat-list li.is-current {
    background-color: var(--accent-soft);
}

.chat-list a {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-decoration: none;
}

.chat-delete {
    border: 0;
    background: none;
    color: var(--muted);
    cursor: pointer;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0 0.2rem;
}

.chat-delete:hover {
    color: #b42318;
}

.report-dock {
    border-top: 1px solid var(--border);
    padding-top: 0.75rem;
}

.report-dock p {
    margin: 0.4rem 0 0;
}

.report-dock .strip-title {
    margin-bottom: 0.5rem;
}

/* The bill box takes its own line under the chat button; the session picker
   and the button share the next. */
.report-bill {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.5rem;
}

.report-bill input {
    flex: 1 1 100%;
    min-width: 0;
    text-transform: uppercase;
}

.report-sessions {
    flex: 1 1 100%;
    font-size: 0.85rem;
}

.report-sessions summary {
    color: var(--muted);
}

/* The session checkboxes inside the chat filter and the report box: a
   compact column rather than Pure's full-width stacked form rows. */
.filter-popover fieldset,
.report-sessions {
    border: 0;
    margin: 0.4rem 0 0;
    padding: 0;
}

.filter-popover legend {
    font-size: 0.9rem;
    padding: 0;
}

.filter-popover .pure-checkbox,
.report-sessions .pure-checkbox {
    display: block;
    margin: 0.15rem 0;
}

#report-bill-picks {
    margin-top: 0.4rem;
}

/* Up to ten hearings, scrolled sideways rather than wrapped, so the strip
   costs the conversation one row whatever it holds. */
.strip-title {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin: 0 0 0.4rem;
}

.relevant-strip {
    margin-bottom: 1rem;
    padding-bottom: 0.75rem;
    border-bottom: 1px solid var(--border);
}

.relevant-cards {
    display: flex;
    gap: 0.5rem;
    list-style: none;
    margin: 0;
    padding: 0 0 0.35rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
}

.relevant-cards li {
    flex: 0 0 13rem;
    scroll-snap-align: start;
}

.relevant-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    box-sizing: border-box;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    line-height: 1.35;
}

.relevant-card:hover {
    background-color: var(--accent-soft);
}

.relevant-card strong {
    color: var(--text);
    font-weight: 600;
    font-size: 0.92rem;
}

.chat-log {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    max-height: 62vh;
    overflow-y: auto;
}

.chat-log:empty {
    display: none;
}

.chat-message {
    max-width: 85%;
    padding: 0.6rem 0.9rem;
    border-radius: var(--radius);
}

.chat-message > :first-child {
    margin-top: 0;
}

.chat-message > :last-child {
    margin-bottom: 0;
}

.chat-user {
    align-self: flex-end;
    background-color: var(--accent-soft);
    white-space: pre-wrap;
}

.chat-assistant {
    align-self: flex-start;
    border: 1px solid var(--border);
}

.chat-assistant.is-pending {
    border-style: dashed;
}

.turn-status:empty {
    display: none;
}

.chat-delete.htmx-request {
    opacity: 0.4;
}

/* A citation: "▶ 20:50" into the video and "¶" into the transcript, as one
   small pill so a paragraph with several stays readable. */
.cite {
    display: inline-flex;
    gap: 0.3rem;
    margin: 0 0.1rem;
    padding: 0 0.35rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 0.75rem;
    line-height: 1.5;
    vertical-align: 0.1em;
    white-space: nowrap;
}

.cite a {
    text-decoration: none;
}

/* The card a citation opens on hover or keyboard focus: the hearing's chair,
   agenda and witness counts. Anchored to the pill, above the line so it does
   not cover the text the reader is on. */
.cite {
    position: relative;
}

.cite-record {
    border-style: dashed;
}

/* The video link inside a pill, only on a hearing's top moments. */
.cite-watch {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* A legislator or bill linked to its page on capitol.texas.gov: underlined
   lightly, so a paragraph full of names does not read as all links. */
.official-link {
    text-decoration: underline;
    text-decoration-style: dotted;
    text-underline-offset: 2px;
}

.cite-card {
    display: none;
    position: absolute;
    bottom: calc(100% + 0.3rem);
    left: 0;
    z-index: 10;
    width: 18rem;
    padding: 0.55rem 0.7rem;
    background-color: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 4px 16px rgba(31, 41, 51, 0.14);
    color: var(--text);
    font-size: 0.8rem;
    line-height: 1.45;
    white-space: normal;
    text-align: left;
}

.cite:hover .cite-card,
.cite:focus-within .cite-card {
    display: block;
}

.cite-card > span,
.cite-card > a {
    display: block;
}

.cite-card-title {
    font-weight: 600;
}

.cite-card-sub {
    color: var(--muted);
    margin-bottom: 0.25rem;
}

.cite-card-link {
    margin-top: 0.35rem;
}

.starters {
    margin-bottom: 1rem;
}

.pill.starter {
    cursor: pointer;
    font: inherit;
    font-size: 0.85rem;
    white-space: normal;
    text-align: left;
}

.pill.starter:hover {
    background-color: var(--accent-soft);
}

.chat-form {
    margin-top: 1rem;
}

.chat-input-row {
    display: flex;
    gap: 0.5rem;
    align-items: flex-end;
}

.chat-input-row textarea {
    flex: 1;
    resize: vertical;
    min-height: 2.6rem;
}

/* The filter menu opens upward, over the conversation, rather than pushing
   the input down the page. */
.filter-menu {
    position: relative;
    display: inline-block;
    margin-bottom: 0.5rem;
    font-size: 0.9rem;
}

.filter-menu summary {
    color: var(--muted);
}

.filter-popover {
    position: absolute;
    bottom: calc(100% + 0.35rem);
    left: 0;
    z-index: 5;
    width: 15rem;
    padding: 0.75rem;
    background-color: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 4px 16px rgba(31, 41, 51, 0.12);
}

@media screen and (max-width: 47.99em) {
    .chat-message {
        max-width: 100%;
    }

    /* Room on a phone is the width of the bubble, not a fixed card. */
    .cite-card {
        width: min(18rem, 80vw);
    }

    .chat-log {
        max-height: none;
    }
}

/* --- Narrow screens --------------------------------------------------- */

/* Everything below the point where the layout is still comfortable in two
   dimensions. Pure's grid columns collapse at 64em, but the nav and the
   results table run out of room well before that. */
@media screen and (max-width: 47.99em) {

    /* Shell: give the content back the padding the desktop can afford. */
    .page {
        padding: 1rem;
    }

    .panel {
        padding: 1rem;
        margin-bottom: 1rem;
    }

    .site-bar .pure-menu-horizontal {
        padding: 0 1rem;
    }

    /* Anything under 16px makes iOS zoom the page on focus. */
    input, select, textarea {
        font-size: 16px;
    }

    .pure-button {
        padding: 0.6em 1em;
    }

    /* Nav: the links hide behind the ☰ label, which toggles the checkbox. */
    .nav-toggle-btn {
        display: block;
        padding: 0.5rem;
        font-size: 1.4rem;
        line-height: 1;
        cursor: pointer;
        color: var(--text);
    }

    .nav-toggle:focus-visible + .nav-toggle-btn {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
    }

    .site-bar .pure-menu-list {
        display: none;
        width: 100%;
    }

    .nav-toggle:checked ~ .pure-menu-list {
        display: block;
        border-top: 1px solid var(--border);
    }

    /* Pure's horizontal menu items are inline-block; stack them into rows
       big enough to tap. */
    .site-bar .pure-menu-item {
        display: block;
    }

    .site-bar .pure-menu-link {
        display: block;
        padding: 0.75rem 0;
    }

    /* Results table: one card per row instead of a sideways scroll. */
    .table-scroll > .cards-sm {
        min-width: 0;
    }

    /* The header row keeps the sort buttons reachable, drawn as a strip. */
    .cards-sm thead tr {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: 0.25rem 0.75rem;
        padding: 0.5rem;
    }

    .cards-sm thead tr::before {
        content: "Sort by";
        color: var(--muted);
        font-size: 0.85rem;
    }

    .cards-sm th {
        display: block;
        padding: 0;
        border: 0;
    }

    .cards-sm tbody tr {
        display: block;
        padding: 0.75rem 0.5rem;
        border-top: 1px solid var(--border);
    }

    .cards-sm td {
        display: block;
        padding: 0.15rem 0;
        border: 0;
        white-space: normal;
    }

    /* The title cell is the card's heading, so it carries no label. */
    .cards-sm td[data-label]:not([data-label=""])::before {
        content: attr(data-label) ": ";
        color: var(--muted);
    }

    /* Hearing details read better stacked than as a squeezed two-column grid. */
    .meta-list {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .meta-list dt {
        margin-top: 0.5rem;
    }

    .meta-list > div:first-child dt {
        margin-top: 0;
    }

    .transcript-line .timestamp {
        width: auto;
    }

    .text-content {
        flex: 1 0 100%;
        min-width: 0;
    }

    /* Anchored to the line's left edge, a fixed-width dropdown would hang
       off the screen. */
    .speaker-dropdown {
        right: 0;
        min-width: 0;
    }
}
