/* ---------------------------------------------------------------------------
   Minimal operator UI.

   Deliberately plain: this is an internal back-office tool used all day by a
   handful of people, so it optimises for density, legibility and speed rather
   than decoration. One accent colour, hairline borders, no shadows, no
   gradients, no icon font. Status is never encoded by colour alone (WCAG 2.1
   AA, 1.4.1) - every pill carries its own text.
   --------------------------------------------------------------------------- */

:root {
  --fg: #16181d;
  --fg-muted: #6b7280;
  --bg: #fff;
  --bg-soft: #fafafa;
  --line: #e6e7ea;
  --line-strong: #d3d5da;
  --accent: #2d5bd7;
  --accent-soft: #eef2fd;
  --ok: #1c7a4a;
  --warn: #8a5b00;
  --error: #b3261e;
  --radius: 6px;
  --gap: 1rem;
  --speed: 120ms;
}

@media (prefers-color-scheme: dark) {
  :root {
    --fg: #e8eaed;
    --fg-muted: #9aa0aa;
    --bg: #16181d;
    --bg-soft: #1c1f26;
    --line: #2a2e37;
    --line-strong: #3a3f4a;
    --accent: #7ea2ff;
    --accent-soft: #1e2740;
    --ok: #63c295;
    --warn: #e0b25e;
    --error: #f2857d;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-weight: 600; line-height: 1.25; margin: 0 0 .75rem; }
h1 { font-size: 1.5rem; letter-spacing: -.01em; }
h2 { font-size: 1.125rem; }
h3 { font-size: .95rem; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

p { margin: 0 0 .75rem; }

.skip-link { position: absolute; left: -9999px; }
.skip-link:focus { left: 1rem; top: 1rem; padding: .5rem; background: var(--bg); z-index: 10; }

main { max-width: 76rem; margin: 0 auto; padding: 1.75rem 1.5rem 4rem; }

/* --- top bar ------------------------------------------------------------- */

.topbar {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  padding: 0 1.5rem; border-bottom: 1px solid var(--line);
  background: var(--bg); position: sticky; top: 0; z-index: 5;
}
.topbar nav { display: flex; gap: .25rem; flex-wrap: wrap; }
.topbar nav a {
  display: block; padding: .85rem .7rem; color: var(--fg-muted);
  border-bottom: 2px solid transparent; transition: color var(--speed) ease;
}
.topbar nav a:hover { color: var(--fg); text-decoration: none; }
.topbar nav a[aria-current="page"] { color: var(--fg); border-bottom-color: var(--accent); }
.topbar__user { display: flex; align-items: center; gap: .6rem; font-size: .85rem; color: var(--fg-muted); }
.topbar__user form { margin: 0; }

/* --- structure ----------------------------------------------------------- */

.page-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.25rem;
}
.muted { color: var(--fg-muted); }
.small { font-size: .85rem; }
.stack > * + * { margin-top: 1.5rem; }
.row { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }

.panel { border: 1px solid var(--line); border-radius: var(--radius); }
.panel > h2, .panel > h3 { padding: .75rem 1rem; margin: 0; border-bottom: 1px solid var(--line); }
.panel__body { padding: 1rem; }

.grid { display: grid; gap: var(--gap); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }
.split { display: grid; gap: 1.5rem; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
@media (max-width: 60rem) { .split { grid-template-columns: 1fr; } }

/* --- metrics ------------------------------------------------------------- */

.metric { border: 1px solid var(--line); border-radius: var(--radius); padding: .9rem 1rem; }
.metric__value { font-size: 1.6rem; font-weight: 600; letter-spacing: -.02em; }
.metric__label { font-size: .8rem; color: var(--fg-muted); }

/* --- tables -------------------------------------------------------------- */

.table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); }
table { width: 100%; border-collapse: collapse; font-size: .9rem; }
caption { text-align: left; padding: .75rem 1rem; color: var(--fg-muted); font-size: .85rem; }
th, td { text-align: left; padding: .6rem .75rem; border-bottom: 1px solid var(--line); vertical-align: top; }
thead th {
  font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--fg-muted); font-weight: 600; background: var(--bg-soft);
  position: sticky; top: 0;
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--bg-soft); }
td.numeric, th.numeric { text-align: right; font-variant-numeric: tabular-nums; }
td.actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
td.actions form { display: contents; }

.thumb { width: 3.5rem; height: 2.5rem; object-fit: cover; border-radius: 3px; background: var(--bg-soft); }

/* --- pills --------------------------------------------------------------- */

.pill {
  display: inline-block; font-size: .75rem; line-height: 1.4; padding: .1rem .45rem;
  border-radius: 999px; border: 1px solid var(--line-strong); color: var(--fg-muted);
  white-space: nowrap;
}
.pill--ok { color: var(--ok); border-color: currentColor; }
.pill--warn { color: var(--warn); border-color: currentColor; }
.pill--error { color: var(--error); border-color: currentColor; }
.pill--accent { color: var(--accent); border-color: currentColor; }
.badge { font-size: .7rem; padding: .1rem .45rem; border-radius: 999px; background: var(--accent-soft); color: var(--accent); }

/* --- forms --------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: .3rem; margin-bottom: 1rem; }
.field > label, .fieldset > legend { font-size: .85rem; font-weight: 500; }
.field .helptext, .helptext { font-size: .8rem; color: var(--fg-muted); }
.fieldset { border: 1px solid var(--line); border-radius: var(--radius); padding: .75rem 1rem; margin: 0 0 1rem; }
.fieldset ul { list-style: none; margin: 0; padding: 0; display: flex; gap: 1rem; flex-wrap: wrap; }
.fieldset label { font-weight: 400; font-size: .9rem; }

/* CheckboxSelectMultiple's wrapper <div> (and each option's <div>) - laid out
   as a wrapping row instead of Django's default one-per-line stack, which is
   what made the platform / property-type checklists take up so much
   vertical space. Tag-qualified on the wrapper rule only: Django applies the
   same widget `attrs` class to the individual <input> elements too, and this
   must not touch their own box layout. */
div.checkbox-row { display: flex; flex-wrap: wrap; gap: .4rem 1.25rem; }
.checkbox-row > div { display: flex; }
.checkbox-row label { display: flex; align-items: center; gap: .35rem; cursor: pointer; }

/* A labelled checkbox *group* (`web/_checkbox_group_field.html`) - the `<p>`
   plays the role `.field > label` plays for a single-input field, since a
   group has no one input a real `<label for>` could point at. */
.field__group-label { font-size: .85rem; font-weight: 500; margin: 0 0 .3rem; }

/* City/district/urban (`_FilterableCheckboxSelectMultiple`): a scrollable,
   filterable checkbox list in place of a bare `<select multiple>`. City alone
   can carry several thousand options (HANDOFF §4.19) - the scroll region caps
   how much vertical space that takes, and `.checkbox-list__filter`
   (injected by static/js/filterable-select.js; absent without JS) narrows it. */
.checkbox-list { border: 1px solid var(--line-strong); border-radius: var(--radius); padding: .5rem; }
.checkbox-list__filter { display: block; width: 100%; margin-bottom: .4rem; }
.checkbox-list__options {
  display: flex; flex-direction: column; gap: .1rem;
  max-height: 11rem; overflow-y: auto;
}
.checkbox-list__row label {
  display: flex; align-items: center; gap: .4rem; cursor: pointer;
  padding: .2rem .3rem; border-radius: 3px; font-size: .88rem;
}
.checkbox-list__row label:hover { background: var(--bg-soft); }
.checkbox-list__empty { margin: .2rem .1rem 0; }

/* --- "publish as whom, to which portals" ---------------------------------
   Two controls side by side rather than a list of accounts: the person is the
   part that has to be found, since an admin is offered everybody's (BR-8), and
   the portals are two checkboxes that never move. Side by side because they
   are one sentence - "publish as X to Y" - and stacked they read as two
   unrelated questions.
   ------------------------------------------------------------------------- */

.repost-controls { display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; align-items: start; }
.repost-controls > .field { margin: 0; min-width: 0; }
/* The name list grows with the team; the portal list never does. */
.repost-controls > .field:first-child { flex: 1 1 16rem; }
.repost-controls > .field:first-child :is(select, .filter-select) { width: 100%; }
.repost-controls > .field + .field { flex: 0 1 auto; }
/* A portal the selected person has no account on. Dimmed *and* disabled, never
   dimmed alone: colour is not a state (WCAG 2.1 AA, 1.4.1), and the checkbox
   being genuinely unclickable is what carries the meaning. */
.checkbox-row label.is-unavailable { opacity: .5; cursor: not-allowed; }

/* A long single-choice list with a filter box above it (`_FilterableSelect`).
   The box is injected by filterable-dropdown.js and is absent without
   JavaScript, so this stacks two controls when there are two and lays out
   correctly when there is one. */
.filter-select { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.filter-select__input { width: 100%; }
.filter-select > select { width: 100%; }

input, select, textarea {
  padding: .45rem .55rem; border: 1px solid var(--line-strong); border-radius: 4px;
  font: inherit; color: inherit; background: var(--bg);
  transition: border-color var(--speed) ease;
}
input:hover, select:hover, textarea:hover { border-color: var(--fg-muted); }
input[type="checkbox"], input[type="radio"] { width: auto; accent-color: var(--accent); }
textarea { resize: vertical; }

:is(a, button, input, select, textarea, summary):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

button, .button {
  display: inline-block; padding: .45rem .8rem; border: 1px solid transparent;
  border-radius: 4px; background: var(--accent); color: #fff; font: inherit;
  cursor: pointer; transition: opacity var(--speed) ease;
}
button:hover, .button:hover { opacity: .88; text-decoration: none; }
button:disabled { opacity: .5; cursor: not-allowed; }
.button--quiet, button.quiet {
  background: transparent; color: var(--fg); border-color: var(--line-strong);
}
.button--small, button.small { padding: .25rem .55rem; font-size: .8rem; }
.button--danger, button.danger { background: var(--error); }

/* --- filter bar ----------------------------------------------------------
   One card holding two tiers: the row an operator uses constantly, and a
   disclosure holding the rest. It is a bordered surface rather than loose
   controls floating above the table so that "the things that decide what is
   in this table" read as one object - the previous naked flex row left the
   filters and the first table header competing for the same visual weight.
   -------------------------------------------------------------------------- */

.filters {
  margin-bottom: 1.25rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
}

/* The always-visible row. `end` puts every control on one baseline whatever
   its label wrapped to - which holds because no primary filter carries help
   text; a hint under one control would push its input up out of the row. */
.filters__bar {
  display: flex; flex-wrap: wrap; align-items: end;
  gap: .6rem .7rem; padding: .85rem 1rem;
}
/* One width for every select, and no growth: a filter bar that stretches its
   controls to fill the row gives a page with two filters a pair of absurdly
   wide boxes, and makes the same control a different size on every page. */
.filters__bar .field { margin: 0; flex: 0 1 11rem; min-width: 8.5rem; }
/* Controls fill their share, so the row reads as columns rather than as a
   ragged line of differently-sized boxes. */
.filters__bar .field > :is(input, select) { width: 100%; }
/* The free-text box is the one exception: it carries three kinds of id plus
   the title, so it takes the slack the selects leave. `min-width` rather than
   a fixed width, so it wraps onto its own line instead of shrinking past
   legibility on a narrow screen. */
.filters__bar .field--wide { flex: 1 1 18rem; min-width: 12rem; }
.filters__actions { display: flex; gap: .4rem; align-items: center; flex: 0 0 auto; }

/* --- the "More filters" disclosure --------------------------------------- */

/* Secondary filters, collapsed by default and reopened whenever any of them is
   in use - an operator must never be left wondering why a list looks short
   because the control narrowing it is folded away. The count badge says how
   many are active even while it is shut. */
.filters__more { border-top: 1px solid var(--line); }

.filters__summary {
  display: flex; align-items: center; gap: .4rem;
  padding: .5rem 1rem;
  font-size: .85rem; color: var(--fg-muted);
  cursor: pointer; user-select: none;
  transition: color var(--speed) ease, background var(--speed) ease;
}
/* Replace the native marker with one we can position and animate. Both forms
   are needed: `::-webkit-details-marker` for older WebKit, `list-style` for
   everything current. */
.filters__summary::-webkit-details-marker { display: none; }
.filters__summary { list-style: none; }
/* A chevron drawn from a rotated border corner - no icon font, no SVG asset,
   in keeping with the rest of this sheet. */
.filters__summary::before {
  content: ""; flex: none;
  width: .42rem; height: .42rem;
  margin-inline-end: .25rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg);
  transition: transform var(--speed) ease;
}
.filters__more[open] > .filters__summary::before { transform: rotate(45deg); }
.filters__summary:hover { color: var(--fg); background: var(--bg-soft); }
.filters__more[open] > .filters__summary {
  color: var(--fg); border-bottom: 1px solid var(--line);
}
.filters__summary-label { font-weight: 500; }
/* Keyboard focus belongs on the whole strip, not on a text-sized sliver of it. */
.filters__summary:focus-visible { outline-offset: -2px; }

/* The open panel. A tinted surface so it reads as a drawer belonging to the
   card above it rather than as a second, unrelated form. */
.filters__panel { padding: 0 1rem 1rem; background: var(--bg-soft); }

/* --- one section of the panel -------------------------------------------- */

/* Grouped, not poured into a single grid: fifteen equally-weighted boxes in
   one flat run have no shape, and nothing in them says that "Currency"
   belongs with "Price". The headings are what make the panel scannable, and
   they are `<legend>`s so the grouping is announced rather than merely drawn.
   `fieldset` carries browser defaults that would break the layout. */
.filter-section { border: 0; margin: 0; padding: .9rem 0 0; min-width: 0; }
.filter-section + .filter-section { border-top: 1px solid var(--line); }
.filter-section__title {
  padding: 0; margin-bottom: .6rem;
  font-size: .72rem; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-muted);
}
/* `auto-fill` rather than a fixed column count: the panel is the same markup
   on a laptop and on a 27" monitor, and the number of columns that fit is a
   property of the viewport, not of the form. */
.filter-section__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: .8rem 1rem;
  align-items: start;
}
.filters__panel .field { margin: 0; min-width: 0; }
.filters__panel .field > :is(input, select) { width: 100%; }

.filters__panel-actions {
  display: flex; gap: .5rem; align-items: center;
  margin-top: 1rem; padding-top: .9rem; border-top: 1px solid var(--line);
}

/* --- a from/to pair ------------------------------------------------------ */

/* `fieldset` carries browser defaults that would break the grid cell. */
.field--range { border: 0; margin: 0; padding: 0; min-width: 0; }
.range { display: flex; align-items: center; gap: .35rem; }
/* `min-width: 0` lets the two inputs actually shrink inside the flex row;
   without it they keep their intrinsic width and overflow the cell - which is
   what a pair of native date inputs, whose intrinsic width is generous, does
   to a 12rem column otherwise. */
.range > input { flex: 1 1 0; min-width: 0; width: 100%; }
.range__dash { flex: none; color: var(--fg-muted); }

/* --- a lone checkbox in a filter grid ------------------------------------ */

/* Box beside its label, as a checkbox reads everywhere else in this sheet.
   The padding stands in for the label line its neighbours have above their
   inputs, so the box lands on the same row as the boxes either side of it
   instead of floating at the top of its cell. */
.field--check { padding-top: 1.35rem; }
.check { display: flex; align-items: center; gap: .45rem; }
.check > input[type="checkbox"] { flex: none; margin: 0; }
.check > label { font-weight: 400; font-size: .9rem; cursor: pointer; }
.field__hint { margin: 0; font-size: .8rem; color: var(--fg-muted); }

.errorlist { list-style: none; margin: .25rem 0 0; padding: 0; color: var(--error); font-size: .85rem; }

/* --- messages ------------------------------------------------------------ */

.messages { list-style: none; padding: 0 1.5rem; margin: 1rem auto 0; max-width: 76rem; }
.message {
  padding: .6rem .8rem; border: 1px solid var(--line-strong);
  border-left-width: 3px; border-radius: 4px; margin-bottom: .5rem; font-size: .9rem;
}
.message--success { border-left-color: var(--ok); }
.message--warning { border-left-color: var(--warn); }
.message--error { border-left-color: var(--error); }
.alert--error { padding: .6rem .8rem; border-radius: 4px; border: 1px solid var(--error); color: var(--error); margin-bottom: 1rem; }

/* --- auth ---------------------------------------------------------------- */

.auth { max-width: 22rem; margin: 5rem auto; padding: 0 1.5rem; }

/* --- listing detail ------------------------------------------------------ */

.attrs { display: grid; grid-template-columns: max-content 1fr; gap: .35rem 1rem; font-size: .9rem; margin: 0; }
.attrs dt { color: var(--fg-muted); }
.attrs dd { margin: 0; }

.chips { display: flex; flex-wrap: wrap; gap: .35rem; list-style: none; padding: 0; margin: 0; }
.chips li { font-size: .8rem; padding: .15rem .5rem; border: 1px solid var(--line); border-radius: 999px; }

.gallery { display: flex; gap: .5rem; overflow-x: auto; padding-bottom: .25rem; }
.gallery img { height: 6rem; border-radius: 4px; background: var(--bg-soft); }

.comment { border-top: 1px solid var(--line); padding: .7rem 0; font-size: .9rem; }
.comment:first-child { border-top: 0; }
.comment__meta { color: var(--fg-muted); font-size: .8rem; display: flex; gap: .5rem; align-items: center; }

/* --- progress ------------------------------------------------------------ */

.progress { height: 4px; background: var(--line); border-radius: 999px; overflow: hidden; }
.progress__bar { height: 100%; background: var(--accent); transition: width 400ms ease; }

.notes { margin: 0; padding-left: 1.1rem; font-size: .85rem; color: var(--fg-muted); }
.notes li { margin-bottom: .2rem; }

/* --- pagination ---------------------------------------------------------- */

.pagination { display: flex; gap: .5rem; align-items: center; justify-content: center; margin-top: 1.25rem; font-size: .9rem; }

.empty { padding: 2.5rem 1rem; text-align: center; color: var(--fg-muted); }

/* --- utilities -----------------------------------------------------------
   A handful of spacing helpers, so templates never need a `style=` attribute.
   The Content-Security-Policy sets `style-src 'self'` with no `unsafe-inline`,
   which blocks inline style attributes outright - a rule that is only useful
   if the markup can actually live within it.
   ------------------------------------------------------------------------- */

/* A link out to a listing on its portal. Marked with a trailing arrow so it is
   distinguishable from the in-app links beside it without relying on colour
   alone (WCAG 2.1 AA, 1.4.1); `aria-hidden` in effect, since generated content
   carries no text of its own for a screen reader to repeat over the
   "opens in a new tab" the link already announces. */
.external::after { content: "\2197"; margin-inline-start: .15em; font-size: .85em; }
.pill .external { color: inherit; }

/* An opaque portal reference (a myhome statement UUID). Monospaced because it
   is a string to be compared character by character or pasted, not read. */
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .9em; }

.mt { margin-top: 1rem; }
.mt-sm { margin-top: .5rem; }
.ml { margin-left: .75rem; }
.text-error { color: var(--error); }

/* --- calendar -------------------------------------------------------------- */
.calendar { width: 100%; border-collapse: collapse; table-layout: fixed; }
.calendar th {
  padding: .4rem; text-align: left; font-size: .8rem; color: var(--fg-muted);
  border-bottom: 1px solid var(--line);
}
.calendar__day {
  vertical-align: top; border: 1px solid var(--line); padding: .35rem;
  width: calc(100% / 7); min-height: 5.5rem;
}
.calendar__day--out { background: var(--bg-soft); color: var(--fg-muted); }
.calendar__day--today { background: var(--accent-soft); }
.calendar__day-head { display: flex; justify-content: flex-end; }
.calendar__day-num { font-size: .8rem; color: var(--fg-muted); padding: .1rem .4rem; border-radius: 999px; }
.calendar__day-num:hover { background: var(--accent-soft); text-decoration: none; }
.calendar__day--today .calendar__day-num { color: var(--accent); font-weight: 600; }
.calendar__events { list-style: none; margin: .3rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .15rem; }
.calendar__event {
  display: block; font-size: .75rem; padding: .1rem .35rem; border-radius: 3px;
  background: var(--accent-soft); color: var(--fg); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.calendar__event:hover { text-decoration: none; opacity: .85; }
.calendar__event--all-day { background: var(--accent); color: #fff; }
.calendar__event-time { color: var(--fg-muted); margin-right: .3rem; }
.calendar__event--all-day .calendar__event-time { color: inherit; }

/* Available to assistive technology, not to sighted users (WCAG 2.1 AA). */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}
