/* DoPolitics stylesheet. Hand-written, no framework and no build step. */

:root {
  --ink: #1b1f24;
  --muted: #5b6470;
  --paper: #f6f4ef;
  --tile: #ffffff;
  --line: #d9d5cb;
  --accent: #1d4ed8;
  --caution: #b45309;
  --caution-bg: #fff7e0;
  --radius: 10px;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.5;
}

/* Before a lookup the page sits low, with the address tile near the middle.
   Once the officials tile holds anything, the padding shrinks and everything
   moves up. */
.page {
  max-width: 72rem;
  margin: 0 auto;
  padding: 18vh 1rem 2rem;
  transition: padding-top 0.4s ease;
}

/* After a lookup, and on the policy pages, the page starts near the top. */
.page:has(#officials-tile:not(:empty)),
.page:has(.policy) {
  padding-top: 2rem;
}

.site-header h1 {
  margin: 0 0 1rem;
  text-align: center;
  font-size: 2rem;
}

.intro {
  max-width: 40rem;
  margin: 0 auto 1.5rem;
  text-align: center;
  color: var(--muted);
}

.site-header h1 a {
  color: inherit;
  text-decoration: none;
}

.site-footer {
  margin-top: 2rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.875rem;
}

.site-footer a {
  color: inherit;
}

/* The privacy policy and terms of use: one readable column of text. */
.policy {
  max-width: 44rem;
  margin: 0 auto;
  padding: 1.25rem 1.5rem 1.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--tile);
}

.policy h2 {
  margin-top: 0;
}

.policy h3 {
  margin: 1.5rem 0 0.5rem;
}

.policy-date {
  margin-top: -0.5rem;
  color: var(--muted);
  font-size: 0.875rem;
}

.policy li + li {
  margin-top: 0.5rem;
}

/* The tiles. One column to start with. When the member tile holds anything,
   the officials tile and the member tile share a row, and the bill tile runs
   the full width underneath. */
.tiles {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "address"
    "officials"
    "member"
    "bill";
  gap: 1rem;
  max-width: 40rem;
  margin: 0 auto;
}

#address-tile {
  grid-area: address;
}

#officials-tile {
  grid-area: officials;
}

#member-tile {
  grid-area: member;
}

#bill-tile {
  grid-area: bill;
}

@media (min-width: 50rem) {
  .tiles:has(#member-tile:not(:empty)) {
    max-width: none;
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
    grid-template-areas:
      "address   address"
      "officials member"
      "bill      bill";
  }
}

/* After a lookup the page is laid out to fit one screen: the greeting steps
   aside, the address form folds into one row, and once a member is chosen
   the officials and member tiles share a fixed height and scroll inside
   themselves. When the bill tile opens, the two above it shrink a little and
   the bill tile gets most of a screen of its own, since it holds the most
   reading; the page scrolls down to it. On a short screen the clamp()
   minimums win. */
.page:has(#officials-tile:not(:empty)) .intro {
  display: none;
}

@media (min-width: 50rem) {
  .tiles:has(#officials-tile:not(:empty)) #address-tile form {
    grid-template-columns:
      minmax(0, 3fr) minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1.5fr)
      auto;
    align-items: end;
  }

  .tiles:has(#officials-tile:not(:empty)) #address-tile .field-street,
  .tiles:has(#officials-tile:not(:empty)) #address-tile .form-actions {
    grid-column: auto;
  }

  .tiles:has(#officials-tile:not(:empty)) #address-tile .hint {
    display: none;
  }
}

.tiles:has(#member-tile:not(:empty)) #officials-tile,
.tiles:has(#member-tile:not(:empty)) #member-tile {
  height: clamp(18rem, 62vh, 44rem);
}

.tiles:has(#bill-tile:not(:empty)) #officials-tile,
.tiles:has(#bill-tile:not(:empty)) #member-tile {
  height: clamp(22rem, 55vh, 40rem);
}

#bill-tile {
  height: clamp(20rem, 70vh, 52rem);
}

#officials-tile,
#member-tile,
#bill-tile {
  /* Once a tile is scrolled to its top or bottom, scrolling further moves
     the page instead (the browser's default, called scroll chaining), so a
     reader never has to move the pointer off a tile to reach the rest of the
     page. */
  overflow-y: auto;
}

.tile {
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem;
}

/* An empty slot takes no room and shows no border. */
.tile:empty {
  display: none;
}

.tile h2 {
  margin: 0 0 0.75rem;
  font-size: 1.125rem;
}

/* The address form. The street takes a full row; city, state and ZIP code
   share the next one. On a narrow screen every field gets its own row. */
#address-tile label {
  display: block;
  margin-bottom: 0.375rem;
  font-weight: 600;
}

#address-tile form {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.75rem;
}

@media (min-width: 30rem) {
  #address-tile form {
    grid-template-columns: minmax(0, 3fr) minmax(0, 1fr) minmax(0, 2fr);
  }

  .field-street,
  .hint,
  .form-actions {
    grid-column: 1 / -1;
  }
}

/* The search button takes the room it is given; Clear stays its own size. */
.form-actions {
  display: flex;
  gap: 0.5rem;
}

.form-actions button[type="submit"] {
  flex: 1;
}

.hint {
  margin: 0;
  color: var(--muted);
  font-size: 0.875rem;
}

input[type="text"] {
  width: 100%;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  font: inherit;
}

button {
  padding: 0.625rem 1rem;
  border: 0;
  border-radius: 6px;
  background: var(--accent);
  color: #ffffff;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

/* Clear is the quieter of the two: an outline instead of a fill. The outline
   is drawn inside the button, so both buttons are the same height. */
button[type="reset"] {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--accent);
  color: var(--accent);
}

input[type="text"]:focus-visible,
button:focus-visible,
a:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* The officials tile. */
.officials {
  margin: 0;
  padding: 0;
  list-style: none;
}

.officials li {
  padding: 0.625rem 0;
  border-top: 1px solid var(--line);
}

.officials li:first-child {
  border-top: 0;
}

/* A photo sits to the left of the name. The img tag's width and height give
   the shape, so the browser keeps the room for it while it loads, and the
   rules below set the size it is shown at. */
.official,
.member-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.photo-small,
.photo-large {
  flex: none;
  height: auto;
  border-radius: 6px;
  background: var(--line);
  object-fit: cover;
}

.photo-small {
  width: 3rem;
}

.photo-large {
  width: 6rem;
}

.member-header h2 {
  margin-bottom: 0.25rem;
}

.member-header .official-role {
  margin: 0;
}

.photo-credit {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* A name, or a vote's bill, is a button that looks like a link: it does
   something on this page and does not go to another one. */
.official-name,
button.vote-title {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
}

.official-role,
.vacant {
  color: var(--muted);
  font-size: 0.9375rem;
}

.message {
  margin: 0;
}

/* The member tile. */
.tile h3 {
  margin: 1.25rem 0 0.5rem;
  font-size: 1rem;
}

.contact,
.attendance,
.votes {
  margin: 0;
  padding: 0;
  list-style: none;
}

.contact li,
.attendance li {
  padding: 0.125rem 0;
}

/* The member's name, contact line and attendance stay pinned to the top of
   the tile while the votes scroll under them. The tile is the scrolling
   box, so the block's negative margins take it out to the tile's edges and
   its own padding puts the text back where it was. A sticky offset counts
   from inside the tile's padding, so top matches that padding to pin the
   block flush to the tile's top edge, with no strip for votes to show in. */
.member-pinned {
  position: sticky;
  top: -1.25rem;
  z-index: 1;
  margin: -1.25rem -1.25rem 0;
  padding: 1.25rem 1.25rem 0.75rem;
  border-bottom: 1px solid var(--line);
  background: var(--tile);
}

.member-pinned .member-header {
  align-items: flex-start;
}

.member-pinned h3 {
  margin-top: 0.75rem;
}

/* Phone, website and contact form share one line, wrapping when narrow. */
.contact-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
  margin-top: 0.375rem;
}

.contact-office {
  margin: 0.125rem 0 0;
}

/* The minimum wage line under the pay, for comparison. */
.pay-compare {
  margin: 0.375rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

/* The search box and the topic dropdown sit side by side, and stack on a
   narrow screen. */
.vote-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  margin-bottom: 0.5rem;
}

.vote-filter {
  display: flex;
  flex: 1 1 14rem;
  align-items: center;
  gap: 0.5rem;
}

.vote-filter label {
  font-weight: 600;
}

.vote-filter input {
  flex: 1;
  min-width: 0;
}

select,
input[type="search"] {
  max-width: 100%;
  padding: 0.375rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--tile);
  font: inherit;
}

.vote {
  padding: 0.625rem 0;
  border-top: 1px solid var(--line);
}

.vote-title {
  font-weight: 600;
}

.vote-detail,
.vote-none {
  color: var(--muted);
  font-size: 0.9375rem;
}

.vote-more {
  padding-top: 0.75rem;
  border-top: 1px solid var(--line);
}

/* The bill tile. */
.bill-label {
  margin: 0 0 0.25rem;
  color: var(--muted);
  font-weight: 600;
}

.bill-facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.25rem 1rem;
  margin: 0;
}

.bill-facts dt {
  font-weight: 600;
}

.bill-facts dd {
  margin: 0;
}

/* How the member a bill was opened from voted on it, set apart from the
   bill's facts with the accent rule the plain-language text uses. */
.member-votes {
  margin-top: 1rem;
  padding: 0.25rem 1rem 0.5rem;
  border-left: 4px solid var(--accent);
}

.tile .member-votes h3 {
  margin-top: 0.5rem;
}

.member-votes ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* A section that folds away under its heading. The heading sits on the
   same line as the arrow. */
.fold {
  margin-top: 1rem;
}

.fold > summary {
  cursor: pointer;
}

.fold > summary h3 {
  display: inline;
}

/* How the parties voted: one row per roll call, laid out like a member's
   vote rows. */
.party-votes {
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
}

.party-vote {
  padding: 0.625rem 0;
  border-top: 1px solid var(--line);
}

.summary {
  border-top: 1px solid var(--line);
}

.summary > summary {
  padding: 0.625rem 0;
  font-weight: 600;
  cursor: pointer;
}

/* A summary is plain text with blank lines between paragraphs, so the
   line breaks it was stored with are kept. */
.summary-text {
  margin: 0 0 0.75rem;
  white-space: pre-wrap;
}

.summary-source {
  color: var(--muted);
  font-size: 0.875rem;
}

/* The approved plain-language summary, set apart from the official text
   by the page's paper colour and an accent rule down its left side. */
.generated {
  margin-top: 1.25rem;
  padding: 0.25rem 1rem 0.75rem;
  border-left: 4px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--paper);
}

.tile .generated h3 {
  margin-top: 0.75rem;
}

.generated h4 {
  margin: 1rem 0 0.25rem;
  font-size: 0.9375rem;
}

.generated-text {
  margin: 0;
}

/* A draft nobody has reviewed yet says so above its text, in a warm color
   apart from the blue of an approved summary. */
.unreviewed-notice {
  margin: 0 0 0.75rem;
  padding: 0.5rem 0.75rem;
  border-left: 4px solid var(--caution);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--caution-bg);
  font-size: 0.9375rem;
}

.generated-label {
  margin: 0.75rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

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