:root { color-scheme: light dark; }

* { box-sizing: border-box; }

body {
  font: 16px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
  margin: 0;
  color-scheme: light dark;
  /* Explicit surface: without these the page inherits an undefined
     background, and form controls below can't reason about contrast. */
  background-color: Canvas;
  color: CanvasText;
}

a { color: inherit; }

/* --- landing / status pages --- */

.landing {
  max-width: 40rem;
  margin: 0 auto;
  padding: 3rem 1.25rem;
}
.landing h1 { font-size: 1.4rem; margin: .5rem 0 .25rem; }
.landing .sub { opacity: .7; margin: 0 0 1.5rem; }
.landing .hint { font-size: .85rem; opacity: .65; margin-top: 2rem; }
.link { text-decoration: underline; }

.button {
  display: inline-block;
  padding: .6rem 1.1rem;
  border-radius: 8px;
  background: #2563eb;
  color: #fff !important;
  text-decoration: none;
  font-weight: 600;
  border: none;
  cursor: pointer;
  font-size: .95rem;
}
.button:hover { background: #1d4ed8; }

/* --- badges --- */

.badge {
  display: inline-block;
  padding: .15rem .55rem;
  border-radius: 99px;
  font-size: .75rem;
  font-weight: 700;
  color: #fff;
  white-space: nowrap;
}
.badge-green  { background: #16a34a; }
.badge-amber  { background: #d97706; }
.badge-blue   { background: #2563eb; }
.badge-red    { background: #dc2626; }
.badge-violet { background: #7c3aed; }

/* --- app shell --- */

/* One measure for every page. This used to be 72rem with an override lifting
   only the subsystem view to 92rem, which is why that one page looked right
   and the review queues and stock — which carry MORE columns, not fewer —
   were the ones overflowing. Still capped: a full-bleed table on an ultrawide
   spreads columns so far apart that following one row across is the hard part.
   (The old data-mode="manufacturing" override was dead — queue pages render
   as data-mode="queue".) */
#root { max-width: 92rem; margin: 0 auto; padding: 0 1.25rem 3rem; }

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
  padding: 1.25rem 0;
  border-bottom: 1px solid rgba(128,128,128,.25);
  margin-bottom: 1rem;
}
.topbar .brand { font-weight: 700; }
.topbar .who { font-size: .85rem; opacity: .8; display: flex; gap: .75rem; align-items: center; flex-wrap: wrap; }

.toolbar {
  display: flex;
  gap: .6rem;
  flex-wrap: wrap;
  align-items: center;
  margin-bottom: 1rem;
}
.toolbar select, .toolbar input[type=search] {
  padding: .4rem .6rem;
  border-radius: 6px;
  border: 1px solid rgba(128,128,128,.4);
  background-color: Canvas;
  color: CanvasText;
  font-size: .9rem;
}
.toolbar input[type=search] { flex: 1; min-width: 12rem; }

.table-wrap { overflow-x: auto; }

table.drawings { width: 100%; border-collapse: collapse; font-size: .9rem; }
table.drawings th, table.drawings td { padding: .55rem .6rem; text-align: left; white-space: nowrap; }

/* Titles are the one free-text column and the one that can be arbitrarily
   long, so a single verbose part name was setting the width of the whole
   table. Truncate with the full text on hover instead. */
td.cell-title, td.cell-notes { max-width: 20rem; overflow: hidden; text-overflow: ellipsis; }
table.drawings th {
  cursor: pointer;
  font-weight: 600;
  border-bottom: 2px solid rgba(128,128,128,.3);
  user-select: none;
}
table.drawings th:hover { opacity: .7; }
table.drawings tbody tr.row { cursor: pointer; border-bottom: 1px solid rgba(128,128,128,.15); }
table.drawings tbody tr.row:hover { background: rgba(128,128,128,.08); }

.empty { opacity: .6; padding: 2rem 0; text-align: center; }
.error { color: #dc2626; }

/* --- modal --- */

.overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,.45);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 2rem 1rem;
  overflow-y: auto;
  z-index: 100;
}
.modal {
  background: Canvas;
  color: CanvasText;
  border-radius: 10px;
  max-width: 62rem;
  width: 100%;
  padding: 1.75rem;
  position: relative;
  box-shadow: 0 10px 40px rgba(0,0,0,.3);
}
.modal .close {
  position: absolute; top: .75rem; right: .9rem;
  background: none; border: none; font-size: 1.4rem; line-height: 1; cursor: pointer;
  color: inherit; opacity: .6;
}
.modal .close:hover { opacity: 1; }
.modal h2 { margin: 0 .5rem .5rem 0; font-size: 1.25rem; }
.modal h3 { font-size: .95rem; margin: 1.5rem 0 .5rem; opacity: .85; }
.modal .meta { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; font-size: .85rem; opacity: .85; margin-bottom: 1rem; }
.modal .preview {
  width: 100%;
  height: 62vh;
  min-height: 20rem;
  border: 1px solid rgba(128,128,128,.3);
  border-radius: 6px;
  background: rgba(128,128,128,.06);
}

/* A note can run to 200 characters; the column shows it without letting it
   set the width of every other column. */
.notes-cell { max-width: 16rem; }
.notes-cell span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Photos share the preview box but must letterbox inside it rather than
   stretch — object-fit is what keeps a 4000px phone picture readable. */
.modal .preview-img { object-fit: contain; display: block; }

/* Thumbnails for a submission carrying several photos. */
.thumb-strip { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .45rem; }
.thumb {
  width: 4rem; height: 3rem; object-fit: cover;
  border-radius: 4px; border: 2px solid transparent;
  opacity: .65; cursor: pointer;
}
.thumb:hover { opacity: 1; }
.thumb-on { opacity: 1; border-color: currentColor; }

/* Expanded: near-fullscreen for reading a dense drawing. */
.modal.modal--full { max-width: 96vw; }
.modal.modal--full .preview { height: 82vh; }

.preview-bar {
  display: flex; align-items: center; gap: .9rem;
  margin-bottom: .4rem; font-size: .85rem;
}
.preview-bar .spacer { flex: 1; }
.preview-bar button {
  background: none; border: none; padding: 0;
  text-decoration: underline; cursor: pointer;
  font: inherit; font-size: .85rem; color: inherit; opacity: .8;
}
.preview-bar button:hover, .preview-bar a:hover { opacity: 1; }
.preview-bar a { opacity: .8; }

.modal form { display: flex; flex-direction: column; gap: .6rem; }
.modal form label { display: flex; flex-direction: column; gap: .25rem; font-size: .85rem; }
.modal form input, .modal form select, .modal form textarea {
  padding: .45rem .55rem;
  border-radius: 6px;
  border: 1px solid rgba(128,128,128,.4);
  background-color: Canvas;
  color: CanvasText;
  font: inherit;
}
.modal form textarea { min-height: 3.5rem; resize: vertical; }
.modal form .checkbox { flex-direction: row; align-items: center; gap: .4rem; }
.modal form button[type=submit] {
  align-self: flex-start;
  padding: .5rem 1rem;
  border-radius: 6px;
  border: none;
  background: #2563eb;
  color: #fff;
  font-weight: 600;
  cursor: pointer;
}
.modal form button[type=button] {
  align-self: flex-start;
  padding: .5rem 1rem;
  border-radius: 6px;
  border: 1px solid rgba(128,128,128,.4);
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.comments { display: flex; flex-direction: column; gap: .6rem; margin-bottom: .75rem; }
.comment { font-size: .85rem; padding: .5rem .6rem; background: rgba(128,128,128,.08); border-radius: 6px; }
.comment p { margin: .2rem 0 0; }

ul.revisions, ul.audit { font-size: .8rem; opacity: .85; padding-left: 1.1rem; margin: 0; }
ul.revisions li, ul.audit li { margin-bottom: .25rem; }

/* --- pinned notes --- */

.notes { margin-top: 1.25rem; }
.notes .note-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin: .25rem 0;
  padding: .5rem .6rem;
  background: rgba(128,128,128,.08);
  border-radius: 6px;
  font-size: .9rem;
}
.notes textarea {
  font: inherit;
  font-size: .9rem;
  padding: .45rem .55rem;
  border-radius: 6px;
  border: 1px solid rgba(128,128,128,.4);
  background-color: Canvas;
  color: CanvasText;
  resize: vertical;
}
.notes button.link {
  background: none; border: none; padding: 0;
  text-decoration: underline; cursor: pointer;
  font: inherit; font-size: .85rem; color: inherit; opacity: .8;
}

/* --- home page --- */

.hero { padding: 2rem 0 1.5rem; }
.hero h1 { font-size: 1.5rem; line-height: 1.3; margin: 0 0 .4rem; max-width: 34rem; }
.hero .sub { opacity: .75; margin: 0 0 1.25rem; max-width: 34rem; }
.hero-actions { display: flex; gap: .6rem; flex-wrap: wrap; }
.button-secondary { background: transparent !important; color: inherit !important; border: 1px solid rgba(128,128,128,.45); }
.button-secondary:hover { background: rgba(128,128,128,.12) !important; }

.section-title {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .05em;
  opacity: .6; margin: 1.5rem 0 .75rem;
}

.announcements { display: flex; flex-direction: column; gap: .75rem; }
.announcement {
  border: 1px solid rgba(128,128,128,.25);
  border-left: 3px solid #2563eb;
  border-radius: 8px;
  padding: .85rem 1rem;
}
.announcement h3 { margin: 0 0 .25rem; font-size: 1rem; }
.announcement .note-text { background: none; padding: 0; margin: 0 0 .4rem; }
/* The compose box is a textarea, so people type paragraphs; without this
   their line breaks collapse into one run-on blob. */
.announcement .note-text { white-space: pre-wrap; overflow-wrap: anywhere; }
.announcement .hint { margin: 0; font-size: .8rem; opacity: .65; }

.new-announcement { margin-top: 1.25rem; }
.new-announcement summary { cursor: pointer; font-size: .9rem; opacity: .8; }

/* Shared by the "post" form and the inline editor on an existing card. These
   were scoped to .new-announcement, so the editor rendered with browser
   defaults — a monospace textarea and labels sitting inline with their
   fields. Any form editing an announcement gets .announcement-form. */
.announcement-form .stack {
  display: flex; flex-direction: column; gap: .6rem;
  margin-top: .85rem; max-width: 32rem;
}
.announcement-form label { display: flex; flex-direction: column; gap: .25rem; font-size: .85rem; }
.announcement-form input, .announcement-form textarea {
  padding: .45rem .55rem; border-radius: 6px;
  border: 1px solid rgba(128,128,128,.4);
  background-color: Canvas; color: CanvasText; font: inherit; font-size: .9rem;
  resize: vertical;
}
/* The editor replaces the card it sits in, so it carries its own top spacing
   rather than inheriting the details/summary gap. */
.announcement-form.announcement .stack { margin-top: 0; }
.announcement-form .form-title {
  font-size: .8rem; text-transform: uppercase; letter-spacing: .04em;
  opacity: .6; margin: 0 0 .2rem;
}
.announcement-form .form-actions { display: flex; gap: .9rem; align-items: center; }
.announcement-form input[type="date"] { max-width: 14rem; }

/* --- notes: two columns of standing context, above the drawing --- */

.notes-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: .75rem;
  margin: 0 0 1.1rem;
}
@media (max-width: 46rem) {
  .notes-grid { grid-template-columns: 1fr; }
}
.notes-grid .notes {
  margin: 0;
  border: 1px solid rgba(128,128,128,.25);
  border-left: 3px solid rgba(128,128,128,.45);
  border-radius: 8px;
  padding: .7rem .85rem;
}
.notes-grid .notes h3 { margin: 0 0 .3rem; font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; opacity: .6; }
.notes-grid .notes .note-text { background: none; padding: 0; margin: 0 0 .3rem; font-size: .88rem; }
.notes-grid .notes .empty { padding: 0; margin: 0 0 .3rem; font-size: .88rem; }
.notes-grid .notes .hint { margin: 0 0 .3rem; font-size: .75rem; }

/* --- anchored-markup call to action --- */

.markup-cta {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  padding: .75rem .9rem; margin-bottom: .9rem;
  border: 1px dashed rgba(128,128,128,.4);
  border-radius: 8px;
}
.markup-cta > div { flex: 1; min-width: 14rem; }
.markup-cta strong { font-size: .9rem; }
.markup-cta .button { white-space: nowrap; padding: .45rem .9rem; font-size: .85rem; }


/* --- dark-mode safety net -------------------------------------------------
   Native <option> popups are painted by the OS. If the select only sets
   color:inherit, a dark page yields light text on the popup's light
   background — text that only appears when hovered. Pin both explicitly. */

select, option, optgroup {
  background-color: Canvas;
  color: CanvasText;
}
input, textarea { color-scheme: light dark; }
::placeholder { color: CanvasText; opacity: .55; }

/* --- process / sourcing controls --- */

.badge-sm { font-size: .65rem; padding: .1rem .4rem; margin-right: .2rem; }
.badge-grey { background: #6b7280; }
/* The subsystem's action card — a gate that stops work until they answer,
   so it sits above the review panel and looks like a prompt, not a footnote. */
.gate-card {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
  border: 1px solid rgba(124,58,237,.5); background: rgba(124,58,237,.08);
  border-radius: 8px; padding: .75rem .9rem; margin-bottom: .9rem;
}
.gate-card > div { flex: 1; min-width: 14rem; }
.gate-card .spacer { flex: 0; }
.gate-card .button { white-space: nowrap; }

/* Withdrawing is destructive, so it gets red — but outlined, and at the foot
   of the panel, so it is findable without competing with the gate card. */
.withdraw-card {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
  border: 1px solid rgba(220,38,38,.45);
  border-radius: 8px; padding: .75rem .9rem; margin-top: 1.5rem;
}
.withdraw-card > div { flex: 1; min-width: 14rem; }
.withdraw-card .button { white-space: nowrap; }
.button-danger { background: #dc2626; }
.button-danger:hover { background: #b91c1c; }
.modal--confirm { max-width: 30rem; margin-top: 12vh; }
.modal--confirm label { display: flex; flex-direction: column; gap: .3rem; font-size: .9rem; }
.modal--confirm textarea { width: 100%; box-sizing: border-box; }
.confirm-actions { display: flex; justify-content: flex-end; gap: .5rem; margin-top: 1rem; }

/* First article ready — a gate, not a stage, so it reads as neither
   in-progress blue nor finished green. */
.badge-purple { background: #7c3aed; }

.process-group {
  display: flex; align-items: center; gap: .9rem; flex-wrap: wrap;
  margin: .2rem 0;
}
.process-group .group-label {
  font-size: .85rem; opacity: .75; min-width: 5.5rem;
}
.modal form .process-group label.checkbox {
  flex-direction: row; align-items: center; gap: .35rem;
  font-size: .85rem; margin: 0;
}

/* Dimensions sit side by side: a block is one measurement in three
   directions, not three unrelated questions. */
.dim-group {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: .6rem; margin: .2rem 0 .4rem;
}
.dim-group label { margin: 0; }

/* Two-state toggle: a part is never "neither in-house nor outsourced". */
.segmented { display: inline-flex; border: 1px solid rgba(128,128,128,.45); border-radius: 999px; overflow: hidden; }
.segmented .seg {
  align-self: auto !important;
  padding: .3rem .85rem !important;
  border: none !important;
  border-radius: 0 !important;
  background: transparent !important;
  color: CanvasText !important;
  font-size: .82rem;
  cursor: pointer;
}
.segmented .seg-on { background: #2563eb !important; color: #fff !important; font-weight: 600; }

/* Persistent inline field error — replaces the browser's native validation
   bubble, which auto-dismisses after a few seconds and can't be extended. */
.field-error {
  color: #dc2626;
  font-size: .8rem;
  margin-top: .15rem;
}
.modal form input.invalid { border-color: #dc2626; }

/* --- 3D print requests --- */

/* Print settings as a two-column definition list: the makerspace reads these
   in one pass before starting a job, so they must not be a paragraph. */
.spec { margin-bottom: 1rem; }
.spec-grid {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .2rem .9rem;
  margin: 0;
  font-size: .9rem;
}
.spec-grid dt { opacity: .65; }
.spec-grid dd { margin: 0; font-weight: 600; }

/* Stands in for the preview iframe when the only document is a mesh, which
   no browser can render inline. */
.model-card {
  display: flex;
  align-items: center;
  gap: .75rem;
  flex-wrap: wrap;
  padding: .75rem .9rem;
  margin-bottom: 1rem;
  border: 1px solid rgba(128,128,128,.3);
  border-radius: 8px;
  background: rgba(124,58,237,.07);
}
.model-card .spacer { flex: 1; }
.model-card .button { padding: .4rem .9rem; font-size: .9rem; }

.topbar .brand-link { color: inherit; text-decoration: none; }
.topbar .brand-link:hover { text-decoration: underline; }

/* Home page: the two request paths are different enough that they read as
   separate actions rather than one button with a mode. */
.start-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .9rem; margin-top: .5rem; }
.start-card {
  display: block;
  padding: 1rem 1.1rem;
  border: 1px solid rgba(128,128,128,.3);
  border-radius: 10px;
  text-decoration: none;
  color: inherit;
  background-color: Canvas;
}
.start-card:hover { border-color: rgba(128,128,128,.6); background: rgba(128,128,128,.07); }
.start-card h3 { margin: 0 0 .25rem; font-size: 1rem; }
.start-card p { margin: 0; font-size: .85rem; opacity: .7; }
.start-card .tag { display: inline-block; margin-bottom: .4rem; }

/* --- app navigation ---
   One nav on every page. Two rows: identity + top-level sections, then an
   optional sub-row for sections that hold more than one page. */

.appnav { border-bottom: 1px solid rgba(128,128,128,.25); margin-bottom: 1.25rem; }

.nav-id {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem;
  padding: 1rem 0 .6rem;
}
.nav-brand { font-weight: 700; color: inherit; text-decoration: none; }
.nav-brand:hover { text-decoration: underline; }
/* The product name carries the shared brand red; "WashU Racing" stays plain
   so the accent reads as "which app", not decoration. Same rule in the
   Safety portal's nav — see docs/design-system.md. */
.nav-brand-accent { color: #dc2626; }

/* One cluster for everything that isn't the brand: the cross-app link, the
   emergency contact, and the who-am-I block, so the row reads as a single
   toolbar instead of items scattered by nav-id's space-between. The
   emergency pill already has its own outline to set it apart, so only the
   who-am-I block gets a hairline divider — one boundary, between "utility
   links" and "your account", not a rule between every item. */
.nav-id-right {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}
.nav-who {
  font-size: .85rem;
  opacity: .8;
  display: flex;
  gap: .75rem;
  align-items: center;
  flex-wrap: wrap;
  padding-left: 1rem;
  border-left: 1px solid rgba(128,128,128,.25);
}

/* Emergency contact: explicit colours rather than Canvas/CanvasText, on
   purpose — this is the one element that must stay legible and unmistakably
   "danger" in both themes rather than blend in with the rest of the nav.
   Never hidden at the mobile breakpoint below, even though other nav
   metadata may wrap or shrink there. Outlined rather than solid-filled so it
   reads as "always there" instead of "something just happened" — matches
   the Safety portal's nav, see docs/design-system.md. */
.nav-emergency {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .3rem .75rem;
  border-radius: 999px;
  border: 1.5px solid #dc2626;
  background: transparent;
  color: #dc2626;
  font-weight: 700;
  font-size: .85rem;
  text-decoration: none;
  white-space: nowrap;
}
.nav-emergency:hover { background: rgba(220,38,38,.12); }

/* Buttons that have to look like links: sign-out must be a POST, and the
   name editor opens a prompt. */
.nav-linkbtn {
  background: none;
  border: none;
  padding: 0;
  text-decoration: underline;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.nav-signout { display: inline; }

.nav-tabs { display: flex; gap: .25rem; flex-wrap: wrap; }
.nav-tab {
  padding: .45rem .85rem;
  border-radius: 8px 8px 0 0;
  text-decoration: none;
  color: inherit;
  opacity: .7;
  font-size: .92rem;
  border-bottom: 2px solid transparent;
}
.nav-tab:hover { opacity: 1; background: rgba(128,128,128,.1); }
/* Accent red rather than a neutral tint, so the current section reads as
   "the brand colour says you're here" the same way the Safety portal's
   pill tabs do — same shape/layout as before, only the colour changed. */
.nav-tab.is-current {
  opacity: 1;
  font-weight: 600;
  border-bottom-color: #dc2626;
  background: rgba(220,38,38,.1);
  color: #dc2626;
}

.nav-sub {
  display: flex;
  gap: 1.1rem;
  flex-wrap: wrap;
  padding: .55rem 0 .2rem;
  font-size: .85rem;
  border-top: 1px solid rgba(128,128,128,.15);
}
.nav-subtab { color: inherit; text-decoration: none; opacity: .65; }
.nav-subtab:hover { opacity: 1; text-decoration: underline; }
.nav-subtab.is-current { opacity: 1; font-weight: 600; text-decoration: underline; }

@media (max-width: 46rem) {
  .nav-tab { padding: .4rem .6rem; font-size: .85rem; }
}

/* Priority cells carry a tooltip explaining where the level came from, so
   even the unbadged levels need to be a hoverable element. */
.plain-cell { cursor: help; }

/* Welding sits between the shop's blue and the makerspace's violet. */
.badge-orange { background: #ea580c; }

/* Numbered setup steps on a page that isn't configured yet. */
.setup-steps { max-width: 40rem; line-height: 1.6; padding-left: 1.2rem; }
.setup-steps li { margin-bottom: .5rem; }

/* A print held because its BOM part has no approved drawing yet. Deliberately
   loud: the makerspace would otherwise find out by clicking Start and getting
   a 409. */
.gate-hold {
  margin: .9rem 0;
  padding: .7rem .85rem;
  border: 1px solid #d97706;
  border-left-width: 4px;
  border-radius: 6px;
  background: rgba(217, 119, 6, .08);
}
.gate-hold h3 { margin: 0 0 .3rem; font-size: .95rem; }
.gate-hold p { margin: 0 0 .3rem; font-size: .9rem; }
.gate-hold p:last-child { margin-bottom: 0; }

/* The stock table is wide enough to scroll on a laptop, and Check in / Check
   out was the thing that fell off the right-hand edge — the one column you
   always need. Pin it so it stays put while the rest scrolls under it.
   An opaque background is required: without it the scrolling cells show
   through the pinned one. */
table.drawings th.col-actions,
table.drawings td.col-actions {
  position: sticky;
  right: 0;
  white-space: nowrap;
  background-color: Canvas;
  box-shadow: -6px 0 6px -6px rgba(0, 0, 0, .35);
}
table.drawings th.col-actions { cursor: default; }
/* The row hover tint is painted on the <tr>, which the opaque pinned cell
   would otherwise hide, making the pinned column look detached from its row. */
table.drawings tbody tr:hover td.col-actions { background-image: linear-gradient(rgba(128,128,128,.08), rgba(128,128,128,.08)); }
