/* ==========================================================================
   DASHBOARD.CSS — the management shell.

   Every colour, radius, space and type value below resolves to a custom
   property declared in the THEME's 00-tokens.css. That file is enqueued as this
   sheet's dependency (see tdc_dash_enqueue), which is what makes the dashboard
   the same brand as the public site rather than a lookalike: change a token and
   both move together.

   No literal hex. No literal px radius. Same rule the public sheets follow.

   Fallbacks are supplied throughout so the shell stays operable even if the
   theme is ever swapped for one that does not declare these tokens — the
   dashboard is how the site is administered, so it cannot be allowed to become
   unusable by a theme change. When the theme IS present its token always wins,
   which is why the fallback values are approximations rather than duplicates.
   ========================================================================== */

/* -------------------------------------------------------------- foundation */

.tdc-dash-body {
  margin: 0;
  background: var(--color-surface-alt, #f7f8fa);
  color: var(--color-text-primary, #0f1724);
  font-family: var(--font-sans, system-ui, sans-serif);
  font-size: var(--text-body, 1rem);
  line-height: var(--leading-body, 1.6);
  -webkit-text-size-adjust: 100%;
}

.tdc-dash-body * {
  box-sizing: border-box;
}

.tdc-dash-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: var(--space-xs, 0.5rem) var(--space-sm, 1rem);
  background: var(--color-primary-700, #002a5e);
  color: var(--color-text-on-dark, #fff);
  text-decoration: none;
}

.tdc-dash-skip:focus {
  left: var(--space-sm, 1rem);
  top: var(--space-sm, 1rem);
}

/* Focus is never removed, only restyled. Keyboard operation of a management
   surface is not optional. */
.tdc-dash-body :focus-visible {
  outline: 3px solid var(--color-accent-strong, #fed216);
  outline-offset: 2px;
}

/* ---------------------------------------------------------- progress bar */

/* Fixed, 3px, above everything. Width is driven by dashboard.js; opacity is
   what fades it out, so the teardown cannot leave a stuck sliver behind. */
.tdc-dash-progress {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 90;
  height: 3px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms linear;
}

.tdc-dash-progress.is-active {
  opacity: 1;
}

.tdc-dash-progress__bar {
  display: block;
  width: 0;
  height: 100%;
  background: var(--color-accent, #fbba1a);
  transition: width 180ms ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .tdc-dash-progress,
  .tdc-dash-progress__bar {
    transition: none;
  }
}

/* ------------------------------------------------------------------ top bar */

.tdc-dash-top {
  display: flex;
  align-items: center;
  gap: var(--space-sm, 1rem);
  padding: var(--space-xs, 0.5rem) var(--space-md, 1.5rem);
  background: var(--color-primary-900, #031932);
  color: var(--color-text-on-dark, #fff);
}

.tdc-dash-top__toggle {
  display: none;
  width: 2.75rem;
  height: 2.75rem;
  padding: 0;
  border: 1px solid var(--color-border-on-dark, rgba(255, 255, 255, 0.24));
  border-radius: var(--radius-sm, 4px);
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.tdc-dash-top__toggle-bars,
.tdc-dash-top__toggle-bars::before,
.tdc-dash-top__toggle-bars::after {
  display: block;
  width: 1.125rem;
  height: 2px;
  margin: 0 auto;
  background: currentColor;
  content: "";
}

.tdc-dash-top__toggle-bars::before {
  transform: translateY(-6px);
}

.tdc-dash-top__toggle-bars::after {
  transform: translateY(4px);
}

.tdc-dash-top__brand {
  display: flex;
  align-items: center;
  gap: var(--space-2xs, 0.375rem);
  color: inherit;
  text-decoration: none;
  font-weight: var(--font-weight-bold, 700);
}

.tdc-dash-top__mark {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-sm, 4px);
  background: var(--color-accent, #fbba1a);
  color: var(--color-accent-on, #0b0a16);
  font-size: var(--text-sm, 0.875rem);
  letter-spacing: 0.04em;
}

.tdc-dash-top__status {
  margin: 0;
  flex: 1 1 auto;
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-on-dark-muted, #dee3e9);
}

.tdc-dash-top__actions {
  display: flex;
  align-items: center;
  gap: var(--space-sm, 1rem);
  font-size: var(--text-sm, 0.875rem);
}

.tdc-dash-top__link,
.tdc-dash-top__signout {
  /* Padded to clear the WCAG 2.2 minimum target height. Unpadded these were 20px
     tall — measured, not assumed — which is below the 24px floor and awkward to
     hit on a phone even though they are secondary controls. */
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target-min, 24px);
  padding: var(--space-3xs, 0.125rem) 0;
  color: var(--color-text-on-dark, #fff);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.tdc-dash-top__link:hover,
.tdc-dash-top__signout:hover {
  border-bottom-color: var(--color-accent-strong, #fed216);
}

.tdc-dash-top__user {
  color: var(--color-text-on-dark-muted, #dee3e9);
}

/* ------------------------------------------------------------------- shell */

.tdc-dash-shell {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  align-items: start;
  min-height: calc(100vh - 3.75rem);
}

.tdc-dash-nav {
  padding: var(--space-sm, 1rem) 0;
  background: var(--color-surface, #fff);
  border-right: 1px solid var(--color-border, #e3e4e7);
  min-height: 100%;
}

.tdc-dash-nav__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tdc-dash-nav__link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs, 0.5rem);
  padding: var(--space-xs, 0.5rem) var(--space-sm, 1rem);
  color: var(--color-text-secondary, #3b4149);
  text-decoration: none;
  border-left: 3px solid transparent;
}

.tdc-dash-nav__link:hover {
  background: var(--color-primary-100, #e3edf8);
  color: var(--color-primary-700, #002a5e);
}

/* The active item is marked by BOTH the bar and the weight change, never by
   colour alone. */
.tdc-dash-nav__link.is-active {
  border-left-color: var(--color-accent, #fbba1a);
  background: var(--color-primary-100, #e3edf8);
  color: var(--color-primary-700, #002a5e);
  font-weight: var(--font-weight-bold, 700);
}

.tdc-dash-nav__count {
  flex: 0 0 auto;
  min-width: 1.75rem;
  padding: 0 var(--space-2xs, 0.375rem);
  border-radius: var(--radius-full, 999px);
  background: var(--color-surface-alt, #f7f8fa);
  color: var(--color-text-muted, #5b6066);
  font-size: var(--text-sm, 0.875rem);
  text-align: center;
}

.tdc-dash-main {
  padding: var(--space-md, 1.5rem);
  min-width: 0;
}

.tdc-dash-main:focus {
  outline: none;
}

.tdc-dash-page {
  max-width: 60rem;
}

/* Skeleton state while a navigation is in flight. Applied by dashboard.js and
   removed unconditionally on pagehide, so it can never strand the UI. */
.tdc-dash-page.is-loading {
  opacity: 0.45;
  pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
  .tdc-dash-page {
    transition: opacity 150ms linear;
  }
}

.tdc-dash-page__header {
  margin-bottom: var(--space-md, 1.5rem);
}

.tdc-dash-page__title {
  margin: 0 0 var(--space-2xs, 0.375rem);
  font-family: var(--font-heading, inherit);
  font-size: var(--text-h2, 1.5rem);
  line-height: 1.2;
  color: var(--color-primary-700, #002a5e);
}

.tdc-dash-page__lead {
  margin: 0;
  max-width: 46rem;
  color: var(--color-text-secondary, #3b4149);
}

/* ------------------------------------------------------------------- stats */

.tdc-dash-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--space-sm, 1rem);
  margin: 0 0 var(--space-lg, 2rem);
  padding: 0;
  list-style: none;
}

.tdc-dash-stat__link {
  display: block;
  padding: var(--space-sm, 1rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-md, 8px);
  background: var(--color-surface, #fff);
  text-decoration: none;
  color: inherit;
}

.tdc-dash-stat__link:hover {
  border-color: var(--color-primary-500, #1263bb);
}

.tdc-dash-stat__value {
  display: block;
  font-family: var(--font-heading, inherit);
  font-size: var(--text-h2, 1.5rem);
  font-weight: var(--font-weight-bold, 700);
  color: var(--color-primary-700, #002a5e);
}

.tdc-dash-stat__label {
  display: block;
  font-weight: var(--font-weight-bold, 700);
}

.tdc-dash-stat__meta {
  display: block;
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-muted, #5b6066);
}

/* ------------------------------------------------------- sections and lists */

.tdc-dash-section {
  margin-bottom: var(--space-lg, 2rem);
  padding: var(--space-md, 1.5rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-md, 8px);
  background: var(--color-surface, #fff);
}

.tdc-dash-section__title {
  margin: 0 0 var(--space-xs, 0.5rem);
  font-family: var(--font-heading, inherit);
  font-size: var(--text-h3, 1.25rem);
  color: var(--color-primary-700, #002a5e);
}

.tdc-dash-section__note {
  margin: 0 0 var(--space-sm, 1rem);
  color: var(--color-text-secondary, #3b4149);
}

.tdc-dash-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tdc-dash-list__item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs, 0.5rem);
  padding: var(--space-xs, 0.5rem) 0;
  border-bottom: 1px solid var(--color-border-subtle, #eff1f3);
}

.tdc-dash-list__item:last-child {
  border-bottom: 0;
}

.tdc-dash-list__title {
  font-weight: var(--font-weight-bold, 700);
  color: var(--color-primary-600, #003c81);
}

.tdc-dash-list__meta {
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-muted, #5b6066);
}

/* Marked by the word "Missing:" in the content AND by weight, never by colour
   alone — the same rule the nav's active state follows. */
.tdc-dash-list__meta--warn {
  color: var(--color-warning, #8a5a00);
  font-weight: var(--font-weight-semibold, 600);
}

/* Wraps a single row action so the button can sit inside the flex row without a
   nested form changing the layout. */
.tdc-dash-inline {
  margin: 0 0 0 auto;
}

/* ----------------------------------------------------------------- notices */

/* Printed by tdc_dash_result_notice() after every save redirect. role=status on
   success and role=alert on failure, so the outcome is announced without the
   user having to hunt for it. */
.tdc-dash-notice {
  margin: 0 0 var(--space-md, 1.5rem);
  padding: var(--space-sm, 1rem) var(--space-md, 1.5rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-left: 4px solid var(--color-success, #1c7a4a);
  border-radius: var(--radius-md, 8px);
  background: var(--color-surface, #fff);
  color: var(--color-text-primary, #0f1724);
  font-weight: var(--font-weight-semibold, 600);
}

.tdc-dash-notice--error {
  border-left-color: var(--color-error, #b32020);
}

/* --------------------------------------------------------- section tabs */

/* One tab per homepage option group. Twelve labels will not fit on one line at
   any usable size, so the strip wraps rather than scrolling: a wrapped row is
   fully visible at a glance, where a scrolling row hides its tail behind a
   gesture the client has no reason to guess at. */
.tdc-dash-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs, 0.25rem);
  margin: 0 0 var(--space-md, 1.5rem);
}

.tdc-dash-tab {
  padding: 0.5rem var(--space-sm, 1rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--btn-radius, var(--radius-sm, 4px));
  background: var(--color-surface, #fff);
  color: var(--color-primary-700, #002a5e);
  font-size: var(--text-ui, 0.8125rem);
  font-weight: var(--font-weight-semibold, 600);
  text-decoration: none;
  white-space: nowrap;
}

.tdc-dash-tab:hover {
  border-color: var(--color-primary-500, #1263bb);
}

.tdc-dash-tab.is-active {
  background: var(--color-primary-600, #003c81);
  border-color: var(--color-primary-600, #003c81);
  color: var(--color-text-on-dark, #fff);
}

/* ------------------------------------------------------------- empty state */

.tdc-dash-empty {
  padding: var(--space-lg, 2rem);
  border: 1px dashed var(--color-border, #e3e4e7);
  border-radius: var(--radius-md, 8px);
  background: var(--color-surface, #fff);
  text-align: center;
}

.tdc-dash-empty__message {
  margin: 0 0 var(--space-sm, 1rem);
  color: var(--color-text-secondary, #3b4149);
}

/* ----------------------------------------------------------------- buttons */

.tdc-dash-btn {
  display: inline-block;
  padding: 0.625rem var(--space-md, 1rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--btn-radius, var(--radius-sm, 4px));
  background: var(--color-surface, #fff);
  color: var(--color-primary-700, #002a5e);
  font: inherit;
  font-size: var(--text-ui, 0.8125rem);
  font-weight: var(--font-weight-semibold, 600);
  text-decoration: none;
  text-align: center;
  cursor: pointer;
}

.tdc-dash-btn--primary {
  background: var(--color-primary-600, #003c81);
  border-color: var(--color-primary-600, #003c81);
  color: var(--color-text-on-dark, #fff);
}

.tdc-dash-btn--primary:hover {
  background: var(--color-primary-700, #002a5e);
}

.tdc-dash-btn--block {
  display: block;
  width: 100%;
}

/* Secondary destructive or reordering actions. Quieter than the default button
   so "Move to trash" never competes with "Save" for attention, but still a real
   button with a real border — not a bare text link. */
.tdc-dash-btn--quiet {
  padding: 0.375rem var(--space-sm, 0.75rem);
  color: var(--color-text-secondary, #3b4149);
  font-size: var(--text-xs, 0.75rem);
}

.tdc-dash-btn--quiet:hover {
  border-color: var(--color-primary-500, #1263bb);
  color: var(--color-primary-700, #002a5e);
}

/* Duplicate submits are blocked in JS by disabling the button; this is the
   visual half of that contract, so a disabled control never looks pressable. */
.tdc-dash-btn[disabled],
.tdc-dash-btn[aria-disabled="true"] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* The action row sits outside the card so the primary button is not read as
   belonging to the last field. Wraps rather than shrinking, because a save
   button that has been squeezed narrow is the one control that must never be
   hard to hit. */
.tdc-dash-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs, 0.5rem);
  margin: 0;
}

/* ------------------------------------------------------------------ fields */

/* Deliberately unstyled, and audited as such: .tdc-dash-form, .tdc-login__form,
   .tdc-dash-nav__item, .tdc-dash-nav__label, .tdc-dash-stat and
   .tdc-dash-top__name carry no declarations because their parent's grid or flex
   context already governs them. They exist as structural hooks and as the
   elements screen readers traverse, not as boxes to paint. Do not add empty
   rules for them. */

.tdc-field {
  margin: 0 0 var(--space-md, 1rem);
}

.tdc-field__label {
  display: block;
  margin-bottom: var(--space-2xs, 0.25rem);
  font-size: var(--text-sm, 0.875rem);
  font-weight: var(--font-weight-semibold, 600);
  color: var(--color-text-primary, #0f1724);
}

.tdc-field__input {
  display: block;
  width: 100%;
  padding: 0.625rem var(--space-sm, 0.75rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface, #fff);
  color: inherit;
  font: inherit;
  /* 16px minimum: anything smaller makes iOS Safari zoom the viewport on
     focus, which throws the layout out on every mobile sign-in. */
  font-size: var(--text-body, 1rem);
}

.tdc-field__input:focus {
  border-color: var(--color-primary-500, #1263bb);
}

.tdc-field__wrap {
  display: flex;
  align-items: stretch;
  gap: var(--space-2xs, 0.25rem);
}

/* Hidden until JS arms it: an inert button that claims to show the password is
   worse than no button at all. */
.tdc-field__reveal {
  display: none;
  flex: 0 0 auto;
  padding: 0 var(--space-sm, 0.75rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface-alt, #f7f8fa);
  color: var(--color-primary-700, #002a5e);
  font: inherit;
  font-size: var(--text-sm, 0.875rem);
  cursor: pointer;
}

.tdc-dash-body.is-enhanced .tdc-field__reveal {
  display: block;
}

.tdc-field--check {
  display: flex;
}

.tdc-field__check {
  display: flex;
  align-items: center;
  gap: var(--space-xs, 0.5rem);
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-secondary, #3b4149);
}

/* A radio group is wrapped in <fieldset> so the group has a programmatic name;
   the browser's default fieldset border and padding are removed because the
   <legend> is already carrying the label styling. */
.tdc-field__set {
  margin: 0;
  padding: 0;
  border: 0;
}

.tdc-field__choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs, 0.5rem) var(--space-md, 1.5rem);
}

/* The whole label is the target, not just the dot, so the tap area clears the
   24px minimum without a fixed height that would clip a wrapped choice. */
.tdc-field__choice {
  display: flex;
  align-items: center;
  gap: var(--space-2xs, 0.25rem);
  min-height: var(--tap-target-min, 1.5rem);
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-secondary, #3b4149);
  cursor: pointer;
}

/* The native control is 13px at default size. A radio's label is the real target
   (see .tdc-field__choice), but a standalone checkbox is wrapped in a <span>, so
   the control itself has to clear the 24px WCAG 2.2 floor — measured at 18px
   before this rule, which failed it. */
.tdc-field__choice input[type="radio"] {
  flex: 0 0 auto;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
}

.tdc-field__check input[type="checkbox"] {
  flex: 0 0 auto;
  width: var(--tap-target-min, 1.5rem);
  height: var(--tap-target-min, 1.5rem);
  margin: 0;
}

/* Referenced by aria-describedby on the control it explains, so it must stay
   legible rather than being shrunk into decoration. */
.tdc-field__help {
  margin: var(--space-2xs, 0.25rem) 0 0;
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-muted, #5b6066);
}

.tdc-field__input--area {
  min-height: 8rem;
  line-height: var(--leading-body, 1.6);
  resize: vertical;
}

/* Dates and bounded numbers get an intrinsic width: a full-width box invites
   the client to expect free text where only a number or a date is accepted. */
.tdc-field__input--short {
  width: auto;
  max-width: 12rem;
}

.tdc-field__input--select {
  /* Room for the native chevron on every platform, and a max so a long
     attachment filename cannot stretch the control past the card. */
  max-width: 100%;
  padding-right: var(--space-lg, 2rem);
}

/* ---------------------------------------------------------------- uploaders */

/* One control, used by every file field on the dashboard: images and PDFs,
   attachment IDs and URLs. The old .tdc-media-field rules were removed with the
   markup they styled — fields.php now renders .tdc-uploader everywhere. */

.tdc-uploader {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs, 0.5rem);
}

/* Fixed frame so swapping between a portrait and a landscape attachment does
   not make the form jump, and so the empty state occupies the same space the
   thumbnail will. object-fit keeps the whole subject visible. */
.tdc-uploader__preview {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 6rem;
  min-height: 6rem;
  padding: var(--space-2xs, 0.25rem);
  overflow: hidden;
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface-alt, #f7f8fa);
}

.tdc-uploader__preview img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* A document has no thumbnail, so the frame widens to fit its filename rather
   than truncating it to a 6rem square that says nothing. */
.tdc-uploader__preview:has(.tdc-uploader__file) {
  width: auto;
  max-width: 100%;
  min-height: 0;
  padding: var(--space-2xs, 0.25rem) var(--space-xs, 0.5rem);
}

.tdc-uploader__file {
  overflow-wrap: anywhere;
  font-size: var(--text-sm, 0.875rem);
}

.tdc-uploader__empty {
  color: var(--color-text-muted, #5c6370);
  font-size: var(--text-sm, 0.875rem);
}

.tdc-uploader__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs, 0.5rem);
}

/* The <label> is the visible button and the <input type="file"> is hidden
   behind it, because a file input cannot be styled reliably across browsers.
   The input is still focusable and still in the form — see below. */
.tdc-uploader__choose {
  display: inline-block;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface, #fff);
  color: var(--color-text, #1d2129);
  font-size: var(--text-sm, 0.875rem);
  line-height: 1.2;
  cursor: pointer;
}

.tdc-uploader__choose:hover {
  border-color: var(--color-accent, #7a1f2b);
  color: var(--color-accent, #7a1f2b);
}

/* NOT display:none and NOT hidden: either would remove the input from the tab
   order and stop it being announced, so the control would be unreachable by
   keyboard. Clipped instead, and revealed on focus by the rule below. */
.tdc-uploader__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Keyboard focus lands on the hidden input, so its focus ring has to appear on
   the label the user can actually see. */
.tdc-uploader__input:focus-visible + .tdc-uploader__choose,
.tdc-uploader__choose:focus-visible {
  outline: 2px solid var(--color-accent, #7a1f2b);
  outline-offset: 2px;
}

/* min-height is the target size, not decoration: WCAG 2.2 2.5.8 asks for 24x24,
   and the checkbox plus its text came to 22px tall on its own. The label IS the
   target here — it wraps the input — so the height belongs on the label.
   probe-dash-viewport.mjs measures this. */
.tdc-uploader__remove {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs, 0.25rem);
  min-height: 24px;
  font-size: var(--text-sm, 0.875rem);
}

/* Doubles as the progress and success region, which is why it is not coloured
   as an error by default. role="alert" on the element announces whatever lands
   in it, so the text has to carry the meaning rather than the colour. */
.tdc-uploader__error {
  margin: 0;
  color: var(--color-text-muted, #5c6370);
  font-size: var(--text-sm, 0.875rem);
}

.tdc-uploader.is-uploading .tdc-uploader__choose {
  opacity: 0.6;
  pointer-events: none;
}

/* A <summary> is the disclosure's own control, so it carries the 24px target
   height rather than relying on the text's line box. padding-block keeps the
   pointer area and the visible text in step. */
.tdc-uploader__library summary {
  min-height: 24px;
  padding-block: 0.125rem;
  font-size: var(--text-sm, 0.875rem);
  cursor: pointer;
}

.tdc-uploader__library[open] summary {
  margin-bottom: var(--space-xs, 0.5rem);
}

.tdc-uploader .tdc-field__help {
  margin-top: 0;
}

/* ------------------------------------------------------- publications rows */

/* Each row is a card so the three fields plus the move controls read as one
   unit; list-style is removed because the <legend> already carries the row
   number, and a marker as well would announce it twice. */
.tdc-pub-rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tdc-pub-row {
  margin-bottom: var(--space-sm, 1rem);
  padding: var(--space-sm, 1rem);
  border: 1px solid var(--color-border-subtle, #eff1f3);
  /* ---- Twelve rows on one screen ----

     The repeater is unlimited, so this has to hold for a member with twelve
     publications as well as for one with none. Two things change at that length,
     and both are real failures rather than polish.

     WHERE ONE ROW ENDS. At three rows the cards are obvious; at twelve, a page of
     identically tinted boxes is a wall. The left edge is a 3px accent rule per
     row — the same thin-indicator role the accent has everywhere else — so the
     boundaries stay countable at a glance without introducing a second border
     weight.

     NOTHING MAY OVERFLOW SIDEWAYS. Every row carries an uploader whose filename
     chip can be long, and a PDF named after a full paper title routinely is. A
     flex or grid item's default min-width:auto refuses to shrink below its
     content, which is the classic source of a horizontal scrollbar on the whole
     form; min-width:0 lets it, and .tdc-uploader__file already breaks the
     filename. probe-dash-viewport.mjs measures document width against viewport
     width, which is what would catch a regression here. */
  border-left: 3px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface-alt, #f7f8fa);
  min-width: 0;
}

.tdc-pub-row:last-child {
  margin-bottom: 0;
}

.tdc-pub-row .tdc-field:last-of-type {
  margin-bottom: 0;
}

/* WHICH ROW YOU ARE IN. The legend is the only per-row landmark, so at twelve
   rows it earns real weight and the accent's own colour. It is a <legend>, so it
   is also what a screen reader announces on entering the fieldset. */
.tdc-pub-row__legend {
  margin-bottom: var(--space-xs, 0.5rem);
  color: var(--color-primary-700, #002a5e);
  font-size: var(--text-sm, 0.875rem);
  font-weight: var(--font-weight-bold, 700);
}

/* The row's own Move / Remove controls. A separate class from the form's action
   row so the row's buttons wrap independently, and so the separating margin is
   not added to every .tdc-dash-actions on the dashboard. :empty because a blank
   spare row renders the container with nothing in it. */
.tdc-pub-row__actions {
  margin-top: var(--space-xs, 0.5rem);
}

.tdc-pub-row__actions:empty {
  margin-top: 0;
}

/* The count, above the list. A fact about the record rather than a heading, so it
   takes the same muted treatment as the section note it follows. */
.tdc-pub__count {
  margin: 0 0 var(--space-sm, 1rem);
  color: var(--color-text-secondary, #3b4149);
  font-size: var(--text-sm, 0.875rem);
  font-weight: var(--font-weight-semibold, 600);
}

/* ADD IS DELIBERATELY PROMINENT. The defect this replaces was an editor believing
   the form accepted only three publications, so the control that disproves it
   must not be a quiet text link at the foot of a long list: it takes the full
   .tdc-dash-btn weight rather than --quiet. The top margin separates it from the
   last row's Remove — two adjacent buttons where one adds and one destroys is
   exactly where a mis-click costs work. */
.tdc-pub__add {
  margin-top: var(--space-xs, 0.5rem);
}

/* Paired with the row's min-width:0 above. Both are needed: the section is itself
   a flex/grid child, and it would otherwise refuse to shrink for the same reason
   its rows would. */
.tdc-pub {
  min-width: 0;
}

/* ------------------------------------------------------- social link rows */

/* The repeatable social-link editor on Settings. Replaces a textarea in which the
   editor had to type "Facebook|https://…", so the visual job here is to make each
   row read as one record with two fields and an action — not as three unrelated
   controls that happen to be adjacent. */
.tdc-social__rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Same card treatment as .tdc-pub-row, including the left accent rule, so the two
   repeaters on this dashboard are recognisably the same pattern. An editor who has
   used one already knows how this behaves. */
.tdc-social__row {
  margin-bottom: var(--space-sm, 1rem);
  padding: var(--space-sm, 1rem);
  border: 1px solid var(--color-border-subtle, #eff1f3);
  border-left: 3px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface-alt, #f7f8fa);
  min-width: 0;
}

.tdc-social__row:last-child {
  margin-bottom: 0;
}

/* PLATFORM, ADDRESS, REMOVE on one line where there is room. The platform column is
   fixed because a select sized to its content jumps width as the selection changes,
   which drags the URL box with it; the URL takes the remaining space with 1fr, and
   the action column is auto so "Remove" is never wrapped mid-word.

   min-width: 0 on the URL cell is load-bearing: a grid item's default min-width:auto
   refuses to shrink below its content, and a long pasted URL would push the row past
   the form. */
.tdc-social__grid {
  display: grid;
  grid-template-columns: 12rem 1fr auto;
  align-items: end;
  gap: var(--space-sm, 1rem);
  min-width: 0;
}

.tdc-social__platform,
.tdc-social__url {
  margin-bottom: 0;
  min-width: 0;
}

/* The action cell keeps the row's baseline even when it is empty — a blank spare row
   renders no Remove, and without this the grid's last track would collapse and the
   two fields would widen, making the spare a different shape from the rows above. */
.tdc-social__actions {
  display: flex;
  align-items: center;
  min-height: 2.5rem;
}

/* Below 640px the three tracks become one. A 12rem select plus a URL box plus a
   button in a 390px viewport leaves the URL about 90px wide, which is unusable for
   the one field that holds a long value. Stacking is the honest answer, and the
   dashboard's own single-column form rhythm already reads this way at that width. */
@media (max-width: 640px) {
  .tdc-social__grid {
    grid-template-columns: 1fr;
    align-items: stretch;
  }

  .tdc-social__actions {
    min-height: 0;
  }
}

/* The count, above the list. Same treatment as .tdc-pub__count: a fact about the
   record rather than a heading. */
.tdc-social__count {
  margin: 0 0 var(--space-sm, 1rem);
  color: var(--color-text-secondary, #3b4149);
  font-size: var(--text-sm, 0.875rem);
  font-weight: var(--font-weight-semibold, 600);
}

/* Prominent, for the same reason the publications Add is: the whole point of this
   change is that the editor can see there is no fixed number of rows. */
.tdc-social__add {
  margin-top: var(--space-xs, 0.5rem);
}

.tdc-social {
  min-width: 0;
}

/* ------------------------------------------------------------- bulk upload */

/* The bulk gallery form, collapsed by default so the ordinary one-photograph path
   stays the primary one. A <details> rather than a toggle button because it works
   with no JavaScript, which is what the whole form is built for. */

.tdc-bulk__summary {
  min-height: 24px;
  padding-block: 0.125rem;
  color: var(--color-primary-700, #002a5e);
  font-size: var(--text-ui, 0.8125rem);
  font-weight: var(--font-weight-semibold, 600);
  cursor: pointer;
}

.tdc-bulk[open] .tdc-bulk__summary {
  margin-bottom: var(--space-sm, 1rem);
}

/* Same card treatment as the publications rows: the caption and the description
   belong to one photograph and have to read as one unit. list-style is removed
   because the <legend> already numbers the row. */
.tdc-bulk__rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.tdc-bulk__row {
  margin-bottom: var(--space-sm, 1rem);
  padding: var(--space-sm, 1rem);
  border: 1px solid var(--color-border-subtle, #eff1f3);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface-alt, #f7f8fa);
}

.tdc-bulk__row:last-child {
  margin-bottom: 0;
}

.tdc-bulk__row .tdc-field:last-of-type {
  margin-bottom: 0;
}

/* ---------------------------------------------------------------- enquiries */

.tdc-enquiry {
  padding: var(--space-sm, 1rem) 0;
  border-bottom: 1px solid var(--color-border-subtle, #eff1f3);
}

.tdc-enquiry:last-of-type {
  border-bottom: 0;
  padding-bottom: 0;
}

.tdc-enquiry__title {
  margin: 0 0 var(--space-2xs, 0.25rem);
  font-family: var(--font-heading, inherit);
  font-size: var(--text-body, 1rem);
  color: var(--color-primary-700, #002a5e);
}

/* Label and value on one line at desktop width, stacked on a phone by the
   flex-wrap — a fixed two-column grid would truncate a long address. */
.tdc-enquiry__field {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs, 0.25rem) var(--space-sm, 1rem);
  margin: 0 0 var(--space-2xs, 0.25rem);
  font-size: var(--text-sm, 0.875rem);
}

.tdc-enquiry__label {
  flex: 0 0 8rem;
  font-weight: var(--font-weight-semibold, 600);
  color: var(--color-text-secondary, #3b4149);
}

/* The sender's own words, kept as a block so line breaks in the message survive
   without the label column squeezing them. */
.tdc-enquiry__body {
  flex: 1 1 20rem;
  min-width: 0;
  white-space: pre-line;
}

/* ---------------------------------------------------------------- media grid */

.tdc-media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--space-sm, 1rem);
  margin: 0;
  padding: 0;
  list-style: none;
}

.tdc-media-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs, 0.125rem);
  padding: var(--space-xs, 0.5rem);
  border: 1px solid var(--color-border, #e3e4e7);
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface, #fff);
  font-size: var(--text-xs, 0.75rem);
}

.tdc-media-item img {
  display: block;
  width: 100%;
  height: 6rem;
  object-fit: cover;
  border-radius: var(--radius-sm, 4px);
}

/* Stands in for the thumbnail a PDF does not have. Same box as the image above
   it so a mixed grid stays on one baseline. */
.tdc-media-item__doc {
  display: grid;
  place-items: center;
  height: 6rem;
  border-radius: var(--radius-sm, 4px);
  background: var(--color-surface-alt, #f7f8fa);
  color: var(--color-text-muted, #5b6066);
  font-weight: var(--font-weight-bold, 700);
  letter-spacing: var(--tracking-wide, 0.04em);
}

/* Long filenames wrap rather than overflowing the tile; anywhere is required
   because an upload name often has no spaces to break at. */
.tdc-media-item__name {
  overflow-wrap: anywhere;
  font-weight: var(--font-weight-semibold, 600);
}

.tdc-media-item .tdc-dash-btn {
  margin-top: auto;
}

/* -------------------------------------------------------------------- login */

.tdc-dash-body--login {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: var(--space-md, 1rem);
  /* Navy field, same family as the public masthead. */
  background:
    linear-gradient(
      160deg,
      var(--color-primary-800, #012146),
      var(--color-primary-900, #031932)
    );
}

.tdc-login {
  width: 100%;
  max-width: 26rem;
}

.tdc-login__card {
  padding: var(--space-xl, 2rem);
  border-radius: var(--radius-lg, 12px);
  background: var(--color-surface, #fff);
  box-shadow: var(--shadow-lg, 0 16px 40px rgba(3, 25, 50, 0.12));
}

.tdc-login__brand {
  display: flex;
  align-items: center;
  gap: var(--space-xs, 0.5rem);
  margin-bottom: var(--space-lg, 1.5rem);
}

.tdc-login__mark {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-md, 8px);
  background: var(--color-primary-700, #002a5e);
  color: var(--color-accent, #fbba1a);
  font-weight: var(--font-weight-bold, 700);
  letter-spacing: 0.04em;
}

.tdc-login__site {
  margin: 0;
  font-size: var(--text-sm, 0.875rem);
  font-weight: var(--font-weight-semibold, 600);
  color: var(--color-text-secondary, #3b4149);
}

.tdc-login__title {
  margin: 0 0 var(--space-2xs, 0.25rem);
  font-family: var(--font-heading, inherit);
  font-size: var(--text-h3, 1.375rem);
  color: var(--color-primary-700, #002a5e);
}

.tdc-login__lead {
  margin: 0 0 var(--space-lg, 1.5rem);
  font-size: var(--text-sm, 0.875rem);
  color: var(--color-text-muted, #5b6066);
}

/* Marked by the left rule AND the message text, never by colour alone. */
.tdc-login__notice {
  margin: 0 0 var(--space-md, 1rem);
  padding: var(--space-xs, 0.5rem) var(--space-sm, 0.75rem);
  border-left: 3px solid var(--color-primary-500, #1263bb);
  background: var(--color-primary-100, #e3edf8);
  font-size: var(--text-sm, 0.875rem);
}

.tdc-login__notice--error {
  border-left-color: var(--color-error, #e11d0b);
  background: var(--color-surface-alt, #f7f8fa);
  color: var(--color-text-primary, #0f1724);
}

.tdc-login__submit {
  margin: var(--space-lg, 1.5rem) 0 0;
}

.tdc-login__links {
  margin: var(--space-md, 1rem) 0 0;
  font-size: var(--text-sm, 0.875rem);
  text-align: center;
  color: var(--color-text-muted, #5b6066);
}

.tdc-login__links a {
  color: var(--color-primary-600, #003c81);
}

/* --------------------------------------------------------------- utilities */

/* Core's own visually-hidden pattern. Declared here because the dashboard does
   not load the public sheets, and the login form's reveal button and the top
   bar's toggle both depend on it. */
.screen-reader-text {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  border: 0;
  white-space: nowrap;
}

/* -------------------------------------------------------------- responsive */

/* The nav becomes a disclosure below 900px, closed by default, opened from the
   top bar — so on a phone the content starts at the top of the viewport instead
   of below nine navigation rows. */
@media (max-width: 900px) {
  .tdc-dash-top__toggle {
    display: block;
  }

  .tdc-dash-shell {
    grid-template-columns: minmax(0, 1fr);
  }

  .tdc-dash-nav {
    display: none;
    min-height: 0;
    border-right: 0;
    border-bottom: 1px solid var(--color-border, #e3e4e7);
  }

  .tdc-dash-nav.is-open {
    display: block;
  }

  /* Without JS the toggle cannot work, so the nav must not start hidden. */
  .tdc-dash-body:not(.is-enhanced) .tdc-dash-nav {
    display: block;
  }

  .tdc-dash-top__status {
    display: none;
  }
}

@media (max-width: 600px) {
  .tdc-dash-top {
    flex-wrap: wrap;
    row-gap: var(--space-2xs, 0.25rem);
  }

  .tdc-dash-top__actions {
    flex-basis: 100%;
    justify-content: space-between;
    font-size: var(--text-xs, 0.75rem);
  }

  .tdc-dash-main {
    padding: var(--space-md, 1rem) var(--space-sm, 0.75rem);
  }

  .tdc-dash-section {
    padding: var(--space-sm, 0.75rem);
  }

  .tdc-login__card {
    padding: var(--space-lg, 1.5rem);
  }

  /* At 390px the save button gets the full width rather than sitting next to a
     secondary link at half size. The primary action is the one control that has
     to be unmissable on a phone. */
  .tdc-dash-actions .tdc-dash-btn {
    flex: 1 1 100%;
  }

  .tdc-dash-tab {
    flex: 1 1 auto;
    padding: 0.5rem var(--space-xs, 0.5rem);
    font-size: var(--text-xs, 0.75rem);
  }

  /* A number spinner or a date box narrower than the text fields beside it
     reads as broken at this width, so the intrinsic-width rule is dropped. */
  .tdc-field__input--short {
    max-width: 100%;
  }

  /* Label above value: an 8rem label column plus a value leaves too little for
     either at 390px. */
  .tdc-enquiry__label {
    flex-basis: 100%;
  }

  /* Two tiles across rather than one: at 390px a single-column media grid means
     scrolling past one thumbnail at a time to find a file. */
  .tdc-media-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}



