/* ==========================================================================
   天府智能绿化系统 — Apple Design System (from DESIGN.md)
   Single accent: Action Blue #0066cc. Elevation via surface change only.
   The one drop-shadow is reserved for product renders resting on a surface.
   ========================================================================== */

:root {
  /* Brand & accent */
  --primary: #0066cc;
  --primary-focus: #0071e3;
  --primary-on-dark: #2997ff;

  /* Surfaces */
  --ink: #1d1d1f;
  --canvas: #ffffff;
  --canvas-parchment: #f5f5f7;
  --surface-pearl: #fafafc;
  --tile-1: #272729;
  --tile-2: #2a2a2c;
  --tile-3: #252527;
  --surface-black: #000000;

  /* Text */
  --body: #1d1d1f;
  --on-dark: #ffffff;
  /* Text on the Action Blue fill. Every .btn, .btn-danger and the submit
     control reference this, so it has to exist: an undefined custom
     property is invalid at computed-value time and silently falls back to
     the inherited colour, which put near-black text on the blue and
     measured 3.02:1. White on --primary measures 5.57:1. */
  --on-primary: #ffffff;
  --body-muted: #cccccc;
  --ink-muted-80: #333333;
  /* Secondary text on light surfaces. 5.38:1 on white, 4.94:1 on parchment,
     so 12-13px supporting copy clears WCAG AA. The faint blue cast ties the
     neutrals to the accent hue instead of leaving them dead grey. */
  --ink-muted-48: #6a6a6f;
  /* Secondary text on the near-black tiles, which need a much lighter value
     than the light-surface neutral: 6.29:1 on --tile-1. */
  --on-dark-muted: #a8a8ac;
  /* Text sitting on photography, where the backdrop is unknowable. */
  --on-media: #ffffff;

  /* Status. Each is paired with an icon or a text label everywhere it is
     used, so colour is never the only carrier of meaning. */
  --danger: #c8102e;
  --danger-hover: #a50d25;
  --success: #1a7d38;
  --warning: #b25000;

  /* Backing scrim for text over imagery. A gradient that never reaches full
     transparency, so white text survives a bright sky behind the photo.
     Measured against the brightest pixel these photographs actually
     produce (219,225,218), the 0.55 end composites to ~2.06:1 and the 0.8
     end to ~6.4:1; the campus captions land at 8.87:1 worst case because
     their crops are darker. Anything relying on this scrim for small text
     must sit in the dense part of the gradient, not the thin end. */
  --scrim-media: linear-gradient(to top, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.55));

  /* Scrim for 12px text that must clear WCAG AA (4.5:1) no matter how pale
     the photograph behind it is. 0.5 is the measured threshold against the
     brightest pixel in the hero photograph; above it, 0.52 drops to 4.41:1.
     Denser than --scrim-media on purpose: this one carries small text. */
  --scrim-caption: linear-gradient(
    to top,
    rgba(8, 8, 10, 0.58),
    rgba(8, 8, 10, 0.58)
  );

  /* Hairlines */
  --divider-soft: #f0f0f0;
  --hairline: #e0e0e0;
  --ring: rgba(0, 0, 0, 0.08);

  /* Radii */
  --r-xs: 5px;
  --r-sm: 8px;
  --r-md: 11px;
  --r-lg: 18px;
  --r-pill: 9999px;

  /* Spacing (Standard 4pt / 8pt rhythmic grid) */
  --sp-3xs: 2px;
  --sp-xxs: 4px;
  --sp-xs: 8px;
  --sp-sm: 12px;
  --sp-md: 16px;
  --sp-lg: 24px;
  --sp-xl: 32px;
  --sp-xxl: 48px;
  --sp-section: 80px;

  --max-content: 1440px;
  --max-reading: 980px;
  --product-shadow: 0 3px 30px 0 rgba(0, 0, 0, 0.22);

  --font-display: "SF Pro Display", system-ui, -apple-system, "Segoe UI",
    "PingFang SC", "Microsoft YaHei", sans-serif;
  --font-text: "SF Pro Text", system-ui, -apple-system, "Segoe UI",
    "PingFang SC", "Microsoft YaHei", sans-serif;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--body);
  font-family: var(--font-text);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--primary);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-display);
  font-weight: 600;
  margin: 0;
  color: var(--ink);
  text-wrap: balance;
}

p,
.plant-card-desc,
.timeline-note {
  text-wrap: pretty;
}

/* ---------- Global nav: pure black, 44px, 12px labels ---------- */

.global-nav {
  background: var(--surface-black);
  color: var(--on-dark);
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  padding: 0 20px;
  white-space: nowrap;
  overflow-x: auto;
}

/* The primary navigation is a real <nav> landmark, so it carries the
   link row and the shared link styling. */
.global-nav nav {
  display: flex;
  align-items: center;
  gap: 20px;
}

.global-nav a,
.global-nav span.nav-static {
  color: var(--on-dark);
  font-size: 12px;
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.12px;
  opacity: 0.9;
  padding: 4px 2px;
  white-space: nowrap;
}

.global-nav a:hover {
  text-decoration: none;
  opacity: 1;
}

.global-nav .nav-brand {
  font-weight: 600;
  letter-spacing: 0;
  font-size: 13px;
}

.global-nav .nav-user-panel {
  display: flex;
  align-items: center;
  gap: 12px;
}

.global-nav a.nav-auth-link {
  color: #fff;
  background: rgba(255, 255, 255, 0.16);
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 500;
  transition: background 0.2s ease;
}

.global-nav a.nav-auth-link:hover {
  background: var(--primary);
  color: #fff;
}

.global-nav span.nav-admin {
  color: #ff9f0a;
  font-weight: 500;
}

.global-nav span.nav-user {
  color: #30d158;
}

.global-nav a.nav-logout {
  color: var(--on-dark-muted);
  font-size: 11px;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.global-nav a.nav-logout:hover {
  color: #fff;
}

/* ---------- Sub nav: frosted parchment, 52px, 21px/600 title ---------- */

.sub-nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(245, 245, 247, 0.8);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--ring);
}

.sub-nav-inner {
  max-width: var(--max-content);
  margin: 0 auto;
  height: 52px;
  padding: 0 var(--sp-xl);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-lg);
}

.sub-nav-title {
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.19;
  letter-spacing: 0.231px;
  color: var(--ink);
  white-space: nowrap;
}

.sub-nav-links {
  display: flex;
  align-items: center;
  gap: var(--sp-lg);
  flex-wrap: wrap;
}

.sub-nav-links a {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink);
}

.sub-nav-links a:hover {
  color: var(--primary);
  text-decoration: none;
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-block;
  font-family: var(--font-text);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--on-primary);
  background: var(--primary);
  border: 1px solid var(--primary);
  border-radius: var(--r-pill);
  padding: 11px 22px;
  cursor: pointer;
  text-align: center;
  transition: opacity 0.2s ease, background-color 0.2s ease;
  -webkit-appearance: none;
  appearance: none;
}

.btn:hover {
  background: var(--primary-focus);
  border-color: var(--primary-focus);
  text-decoration: none;
  opacity: 0.92;
}

.btn:active {
  transform: scale(0.95);
  background: var(--primary);
  border-color: var(--primary);
  opacity: 1;
}

.btn:focus-visible {
  outline: 2px solid var(--primary-focus);
  outline-offset: 2px;
}

/* ---------- Buttons: full state set ----------
   primary / secondary / tertiary / danger, each with idle, hover, active,
   focus-visible, disabled and loading. Motion is opt-in via
   prefers-reduced-motion: no-preference, so the static page is the default. */

.btn:disabled,
.btn[aria-disabled="true"] {
  background: var(--divider-soft);
  border-color: var(--divider-soft);
  color: var(--ink-muted-48);
  cursor: not-allowed;
  opacity: 1;
  transform: none;
  box-shadow: none;
}

.btn:disabled:hover,
.btn[aria-disabled="true"]:hover {
  background: var(--divider-soft);
  border-color: var(--divider-soft);
  color: var(--ink-muted-48);
}

.btn-loading {
  position: relative;
  color: transparent;
  pointer-events: none;
}

.btn-loading::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: var(--on-primary);
  border-radius: 50%;
}

@media (prefers-reduced-motion: no-preference) {
  .btn-loading::after {
    animation: spin 800ms linear infinite;
  }
}

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

/* Danger: destructive row action. Never the default path. */
.btn-danger,
.btn-danger-solid {
  background: var(--danger);
  color: var(--on-primary);
  border-color: var(--danger);
}

.btn-danger {
  background: transparent;
  color: var(--danger);
  border-color: transparent;
  font-size: 14px;
  font-weight: 400;
  letter-spacing: -0.224px;
  padding: 8px 12px;
}

.btn-danger:hover {
  background: rgba(200, 16, 46, 0.08);
  color: var(--danger);
  border-color: transparent;
}

.btn-danger-solid:hover {
  background: var(--danger-hover);
  border-color: var(--danger-hover);
  color: var(--on-primary);
}

.btn-danger:focus-visible,
.btn-danger-solid:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: 2px;
}

.btn-danger-solid:focus-visible {
  outline-color: var(--danger);
}

/* Motion: press feedback only, and only when motion is welcome. */
@media (prefers-reduced-motion: no-preference) {
  .btn {
    transition: background-color 150ms ease-out, border-color 150ms ease-out,
      color 150ms ease-out, transform 150ms ease-out;
  }

  .btn:active {
    transform: scale(0.97);
  }

  .global-nav a,
  .sub-nav-links a,
  .text-link,
  .footer-links a,
  table.data .actions a {
    transition: color 150ms ease-out, opacity 150ms ease-out;
  }

  /* Focus rings arrive, they do not snap. Ring drawing is a state change
     the keyboard user needs to perceive, so it gets the 150ms baseline. */
  a,
  button,
  input,
  select,
  textarea,
  [tabindex] {
    transition: outline-color 150ms ease-out, outline-offset 150ms ease-out,
      border-color 150ms ease-out, background-color 150ms ease-out;
  }

  .btn,
  .text-link,
  .global-nav a,
  .sub-nav-links a,
  table.data .actions a,
  .footer-links a {
    transition-property: outline-color, outline-offset, border-color,
      background-color, color, opacity, transform;
    transition-duration: 150ms;
    transition-timing-function: ease-out;
  }

  /* Compare surface: the row under the pointer lifts just enough to keep
     the eye anchored while scanning. Background only, never a shadow. */
  table.data tbody tr {
    transition: background-color 150ms ease-out;
  }

  /* The frosted sub-nav gains its blur on scroll rather than paying for a
     backdrop-filter on every frame of a static page. */
  .sub-nav {
    transition: background-color 250ms ease-out, box-shadow 250ms ease-out;
  }

  .sub-nav.is-stuck {
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- Staged entrances ----------
   Transform and opacity only. The hero chunks arrive in reading order with
   restrained stagger; the total budget stays under 500ms so the operator is
   never waiting to work. */

@media (prefers-reduced-motion: no-preference) {
  .hero-copy .eyebrow,
  .hero-copy h1,
  .hero-copy .lead,
  .hero-copy .btn-row,
  .hero-media {
    animation: rise-in 400ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  }

  .hero-media {
    animation-delay: 0ms;
  }

  .hero-copy .eyebrow {
    animation-delay: 60ms;
  }

  .hero-copy h1 {
    animation-delay: 120ms;
  }

  .hero-copy .lead {
    animation-delay: 180ms;
  }

  .hero-copy .btn-row {
    animation-delay: 240ms;
  }

  /* Page header on inner screens: one short entrance, no choreography. */
  .page-head-titles,
  .page-head .search-row,
  .page-work-inner {
    animation: rise-in 300ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  }

  .page-head .search-row {
    animation-delay: 60ms;
  }

  .page-work-inner {
    animation-delay: 100ms;
  }

  /* Empty state: teaches the space, so it is allowed to arrive. */
  .empty-card .empty-state {
    animation: rise-in 300ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  }

  .empty-card .empty-hint {
    animation: rise-in 300ms cubic-bezier(0.16, 1, 0.3, 1) 80ms backwards;
  }

  .empty-card .btn {
    animation: rise-in 300ms cubic-bezier(0.16, 1, 0.3, 1) 150ms backwards;
  }

  /* Validation error: brief, useful, never theatrical. */
  .field-error {
    animation: error-in 200ms cubic-bezier(0.2, 0, 0, 1) backwards;
  }
}

@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes error-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}
   Replaces the native confirm() on destructive links. Traps focus, closes on
   Escape, and restores focus to the trigger. */

.confirm-backdrop[hidden] {
  display: none;
}

.confirm-backdrop {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-lg);
  background: rgba(0, 0, 0, 0.32);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}

.confirm-card {
  background: var(--canvas);
  border-radius: var(--r-lg);
  padding: var(--sp-xl);
  max-width: 400px;
  width: 100%;
}

.confirm-card h2 {
  font-size: 21px;
  font-weight: 600;
  line-height: 1.19;
  letter-spacing: 0.231px;
  margin: 0 0 var(--sp-xs);
  color: var(--ink);
}

.confirm-card p {
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--ink-muted-80);
  margin: 0 0 var(--sp-lg);
}

.confirm-actions {
  display: flex;
  gap: var(--sp-sm);
  justify-content: flex-end;
  flex-wrap: wrap;
}

@media (prefers-reduced-motion: no-preference) {
  .confirm-backdrop {
    animation: fade-in 200ms ease-out;
  }

  .confirm-card {
    animation: card-in 250ms cubic-bezier(0.2, 0, 0, 1);
  }

  /* Exits are softer and shorter than entrances. */
  .confirm-backdrop.is-closing {
    animation: fade-out 150ms ease-in forwards;
  }

  .confirm-backdrop.is-closing .confirm-card {
    animation: card-out 150ms ease-in forwards;
  }
}

@keyframes fade-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@keyframes card-out {
  from {
    opacity: 1;
    transform: scale(1) translateY(0);
  }

  to {
    opacity: 0;
    transform: scale(0.97) translateY(6px);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes card-in {
  from {
    opacity: 0;
    transform: scale(0.95) translateY(8px);
  }

  to {
    opacity: 1;
    transform: scale(1) translateY(0);
  }
}

@media (max-width: 640px) {
  .confirm-actions {
    flex-direction: column-reverse;
  }

  .confirm-actions .btn {
    width: 100%;
  }
}

/* ---------- Focus system ----------
   Every interactive element gets a ring that clears 3:1 against its own
   surface. The global-nav sits on pure black, so it uses the light
   Sky Link Blue; everything else uses Focus Blue.
   Specificity note: this is a plain selector list, not :where(), because
   :where() carries zero specificity and loses to the UA focus-visible rule. */

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--primary-focus);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

.global-nav a:focus-visible,
.sub-nav a:focus-visible {
  outline: 2px solid #7fc4ff;
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible,
.search-input:focus-visible {
  outline: 2px solid var(--primary-focus);
  outline-offset: 1px;
}

.radio-row input[type="radio"]:focus-visible,
.radio-row input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--primary-focus);
  outline-offset: 2px;
}

/* ---------- Touch targets ----------
   The visible label can stay small; the hit area is expanded with a
   pseudo-element so adjacent targets never overlap. */

.global-nav a,
.sub-nav-links a {
  position: relative;
}

.global-nav a::after,
.sub-nav-links a::after {
  content: "";
  position: absolute;
  inset-block: -50%;
  inset-inline: -8px;
}

.footer-links a,
.text-link,
.cell-action {
  position: relative;
  display: inline-block;
}

.footer-links a::after,
.text-link::after {
  content: "";
  position: absolute;
  inset-block: -10px;
  inset-inline: -4px;
}

/* Row actions in data tables: 24px floor without changing the row rhythm. */
table.data .actions a {
  position: relative;
  display: inline-block;
  padding-block: 4px;
}

table.data .actions a::after {
  content: "";
  position: absolute;
  inset-block: -8px;
  inset-inline: -6px;
}

/* Coarse pointers get the full 44px floor on the primary controls. */
@media (pointer: coarse) {
  .btn,
  .search-input {
    min-height: 44px;
  }

  .global-nav a::after,
  .sub-nav-links a::after {
    inset-block: -100%;
  }

  table.data .actions a::after {
    inset-block: -14px;
  }
}

.btn-secondary {
  background: transparent;
  color: var(--primary);
  border: 1px solid var(--primary);
}

.btn-secondary:hover {
  background: rgba(0, 102, 204, 0.06);
  color: var(--primary);
}

.btn-store {
  font-size: 18px;
  font-weight: 300;
  line-height: 1;
  letter-spacing: 0;
  padding: 14px 28px;
}

.btn-dark {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--on-dark);
  background: var(--ink);
  border-color: var(--ink);
  border-radius: var(--r-sm);
  padding: 8px 15px;
}

.btn-dark:hover {
  background: #000;
  border-color: #000;
}

.btn-pearl {
  font-size: 14px;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  background: var(--surface-pearl);
  border: 3px solid var(--divider-soft);
  border-radius: var(--r-md);
  padding: 8px 14px;
}

.btn-pearl:hover {
  background: var(--divider-soft);
  color: var(--ink);
  border-color: var(--divider-soft);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm);
  align-items: center;
}

.btn-row .btn {
  min-height: 44px;
}

/* ---------- Product tiles: full-bleed, 80px air, color IS the divider ---------- */

.tile {
  padding: var(--sp-section) var(--sp-xl);
  text-align: center;
}

.tile-inner {
  max-width: var(--max-reading);
  margin: 0 auto;
}

.tile-hero {
  background: var(--canvas);
  color: var(--ink);
}

.tile-parchment {
  background: var(--canvas-parchment);
  color: var(--ink);
}

.tile-dark {
  background: var(--tile-1);
  color: var(--on-dark);
}

.tile-dark-2 {
  background: var(--tile-2);
  color: var(--on-dark);
}

.tile-dark-3 {
  background: var(--tile-3);
  color: var(--on-dark);
}

.tile-dark h1,
.tile-dark-2 h1,
.tile-dark-3 h1,
.tile-dark h2,
.tile-dark-2 h2,
.tile-dark-3 h2,
.tile-dark h3,
.tile-dark-2 h3,
.tile-dark-3 h3 {
  color: var(--on-dark);
}

.tile p {
  color: inherit;
}

.tile .display-lg {
  font-size: 40px;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: 0;
  margin: 0 0 var(--sp-xs);
}

.tile .lead {
  font-size: 28px;
  font-weight: 400;
  line-height: 1.14;
  letter-spacing: 0.196px;
  margin: 0 auto var(--sp-lg);
  max-width: 720px;
}

.tile .lead-airy {
  font-size: 24px;
  font-weight: 300;
  line-height: 1.5;
  letter-spacing: 0;
  margin: 0 auto var(--sp-lg);
  max-width: 720px;
}

.tile-dark .lead,
.tile-dark-2 .lead,
.tile-dark-3 .lead,
.tile-dark .lead-airy,
.tile-dark-2 .lead-airy,
.tile-dark-3 .lead-airy {
  color: var(--body-muted);
}

.tile .tagline {
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.19;
  letter-spacing: 0.231px;
  margin: 0 0 var(--sp-md);
}

.tile .eyebrow {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink-muted-48);
  margin: 0 0 var(--sp-xs);
}

.tile-dark .eyebrow,
.tile-dark-2 .eyebrow,
.tile-dark-3 .eyebrow {
  color: var(--body-muted);
}

.tile .hero-display {
  font-size: 56px;
  font-weight: 600;
  line-height: 1.07;
  letter-spacing: -0.28px;
  margin: 0 0 var(--sp-xs);
}

.tile .caption {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-48);
}

.tile .btn-row {
  justify-content: center;
  margin-top: var(--sp-lg);
}

/* Page-shell modifiers: a page header tile + a body tile. */
.tile-head {
  padding-bottom: var(--sp-xl);
}

.tile-body {
  padding-top: 0;
}

.tile-head .lead-airy {
  margin-bottom: var(--sp-lg);
}

.tile-head .lead-airy.tight {
  margin-bottom: 0;
}

.text-link {
  color: var(--primary);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
}

.text-link-on-dark {
  color: var(--primary-on-dark);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
}

/* The single drop-shadow: product renders only. */
.product-render {
  display: block;
  margin: var(--sp-xxl) auto 0;
  width: 100%;
  max-width: 720px;
  height: auto;
  border-radius: 0;
  box-shadow: var(--product-shadow);
}

/* ---------- Inner page shell ----------
   Compare/Operate pages: the page title and the search control share one
   left-aligned band, and the data surface uses the full content width.
   The eye lands on the title at the start edge, then drops to the rows. */

.page-head {
  padding: var(--sp-xxl) var(--sp-xl) var(--sp-lg);
  background: var(--canvas);
  border-bottom: 1px solid var(--divider-soft);
}

.page-head-inner {
  max-width: var(--max-content);
  margin: 0 auto;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-lg) var(--sp-xl);
  flex-wrap: wrap;
}

/* The titles keep their measure; the search takes the rest but never
   squeezes the title block, so the placeholder stays readable. */
.page-head-titles {
  flex: 1 1 320px;
  min-width: 0;
}

.page-head .eyebrow {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink-muted-48);
  margin: 0 0 var(--sp-xxs);
}

.page-head h1 {
  font-size: 34px;
  font-weight: 600;
  line-height: 1.24;
  letter-spacing: -0.374px;
  margin: 0;
  color: var(--ink);
}

.page-head .page-sub {
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--ink-muted-80);
  margin: var(--sp-xs) 0 0;
  max-width: 56ch;
}

.page-head .search-row {
  margin: 0;
  flex: 1 1 380px;
  flex-wrap: nowrap;
  align-items: center;
  min-width: 0;
  max-width: 500px;
}

.page-head .search-field {
  flex: 1 1 320px;
  min-width: 240px;
  max-width: 420px;
}

.page-head .search-input {
  width: 100%;
  min-width: 0;
}

.page-head .search-row .btn {
  width: auto;
  flex: none;
}

.page-work {
  padding: var(--sp-xl);
  background: var(--canvas-parchment);
}

.page-work-inner {
  max-width: var(--max-content);
  margin: 0 auto;
}

.page-work .form-card {
  max-width: 720px;
  margin: 0 auto;
}

/* ---------- Skip link ----------
   First tab stop. Off-screen until focused, then pinned to the top-left so
   a keyboard user can bypass the two nav bars. */

/* In-page anchor targets must clear the sticky nav when scrolled to. */
[id] {
  scroll-margin-top: 112px;
}

#main:focus,
#main:focus-visible {
  outline: none;
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--sp-sm);
  inset-block-start: -60px;
  z-index: 200;
  padding: 10px 16px;
  background: var(--canvas);
  color: var(--primary);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.224px;
  text-decoration: none;
  transition: inset-block-start 150ms ease-out;
}

.skip-link:focus {
  inset-block-start: var(--sp-xs);
  text-decoration: none;
}

/* Headings receive focus programmatically after navigation; they should not
   draw a ring for that, only for real keyboard stops. */
.page-head h1:focus,
.hero-copy h1:focus {
  outline: none;
}

/* ---------- Success feedback ----------
   A polite live region that confirms the previous action. It sits below the
   nav, spans the content width, and never steals focus. */

.flash-region {
  position: fixed;
  inset-block-start: 60px;
  inset-inline: 0;
  z-index: 90;
  margin: 0 auto;
  max-width: 420px;
  padding: 12px 20px;
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  color: var(--ink);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  text-align: center;
  cursor: pointer;
}

.flash-region::before {
  content: "\2713";
  display: inline-block;
  margin-inline-end: var(--sp-xs);
  color: var(--success);
  font-weight: 700;
}

.flash-region[hidden] {
  display: none;
}

@media (prefers-reduced-motion: no-preference) {
  .flash-region.is-in {
    animation: flash-in 250ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  .flash-region.is-out {
    animation: flash-out 200ms ease-in forwards;
  }
}

@keyframes flash-in {
  from {
    opacity: 0;
    transform: translateY(-12px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes flash-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@media (max-width: 640px) {
  .flash-region {
    inset-block-start: 56px;
    margin: 0 var(--sp-md);
    max-width: none;
  }
}

/* ---------- Dashboard ----------
   The home surface is a monitoring board, not a poster. It answers three
   questions in order: how many things are under care, what happened most
   recently, and which plant has gone longest without attention. */

.dash {
  padding: var(--sp-lg) var(--sp-xl) var(--sp-xxl);
  background: var(--canvas-parchment);
}

.dash-inner {
  max-width: var(--max-content);
  margin: 0 auto;
}

/* Board header: identity plus the read time. */
.dash-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-lg) var(--sp-xl);
  flex-wrap: wrap;
  margin-bottom: var(--sp-xl);
}

.dash-head h2 {
  font-size: 48px;
  font-weight: 600;
  line-height: 1.09;
  letter-spacing: -0.374px;
  margin: 0;
  color: var(--ink);
}

.dash-head p {
  font-size: 17px;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--ink-muted-80);
  margin: var(--sp-xs) 0 0;
}

.dash-clock {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-48);
  text-align: end;
  white-space: nowrap;
}

.dash-clock strong {
  display: block;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: 0.231px;
  color: var(--ink);
  font-variant-numeric: numerator;
}

/* Metric row: oversized figures separated by hairlines. No card boxes —
   the number is the design, and a border around it would only add chrome. */
.stat-row {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0;
  margin-bottom: var(--sp-xxl);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
}

.stat {
  padding: var(--sp-xl) var(--sp-lg) var(--sp-xl);
  border-inline-end: 1px solid var(--hairline);
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.stat:last-child {
  border-inline-end: none;
}

.stat-label {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
  margin-bottom: var(--sp-xs);
}

.stat-value {
  font-size: 64px;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.374px;
  color: var(--ink);
  font-variant-numeric: numerator;
  display: flex;
  align-items: baseline;
  gap: 6px;
}

.stat-unit {
  font-size: 21px;
  font-weight: 400;
  line-height: 1.19;
  letter-spacing: 0.231px;
  color: var(--ink-muted-48);
}

.stat-foot {
  font-size: 13px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-48);
  margin-top: var(--sp-xs);
}

.stat-foot.is-warn {
  color: var(--warning);
  font-weight: 600;
}

/* Dot marker next to a metric label. Paired with text, never alone. */
.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
}

.dot-leaf {
  background: var(--success);
}

.dot-crew {
  background: var(--primary);
}

.dot-care {
  background: var(--warning);
}

.dot-warn {
  background: var(--danger);
}

/* Board body: main column of panels plus a side rail. The rail stretches so
   the two columns end on the same line instead of leaving a ragged edge. */
.dash-body {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: var(--sp-md);
  align-items: stretch;
}

.dash-col {
  display: flex;
  flex-direction: column;
  gap: var(--sp-md);
  min-width: 0;
}

.dash-rail {
  display: grid;
  gap: var(--sp-md);
  align-content: start;
}

/* Panels are sections, not cards: no border, no radius, no fill. Space and a
   hairline under the heading do the grouping, the way apple.com separates
   its tiles. A box around data would add chrome without adding meaning. */
.panel {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  min-width: 0;
}

.dash-col .panel + .panel,
.panel + .panel {
  margin-top: var(--sp-xl);
}

/* The roster carries the board's main reading surface, so it absorbs the
   spare height instead of leaving the column short. */
.dash-col .panel-grow {
  flex: 1 1 auto;
}

.panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-md);
  margin-bottom: var(--sp-md);
  padding-bottom: var(--sp-xs);
  border-bottom: 1px solid var(--hairline);
}

.panel-head h2 {
  font-size: 24px;
  font-weight: 600;
  line-height: 1.19;
  letter-spacing: 0.231px;
  margin: 0;
  color: var(--ink);
}

.panel-head .text-link {
  font-size: 14px;
  font-weight: 400;
  letter-spacing: -0.224px;
  white-space: nowrap;
}

/* Plant roster: each row is a plant with a coverage meter. */
.plant-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 180px 116px;
  align-items: center;
  gap: var(--sp-lg);
  padding: var(--sp-md) 0;
  border-bottom: 1px solid var(--divider-soft);
}

.plant-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.plant-name {
  font-size: 19px;
  font-weight: 600;
  line-height: 1.24;
  letter-spacing: -0.224px;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.plant-meta {
  font-size: 13px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-48);
  margin-top: 3px;
  overflow-wrap: anywhere;
}

/* Coverage meter: a bar plus its own text label, so the state never
   depends on the fill alone. */
.meter {
  display: block;
  min-width: 0;
}

.meter-track {
  display: block;
  height: 8px;
  border-radius: var(--r-pill);
  background: var(--divider-soft);
  overflow: hidden;
}

.meter-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--success);
  min-width: 3px;
}

.meter-fill.is-low {
  background: var(--warning);
}

.meter-fill.is-none {
  background: var(--danger);
}

.meter-label {
  display: block;
  font-size: 12px;
  line-height: 1.3;
  letter-spacing: -0.12px;
  color: var(--ink-muted-80);
  margin-top: 5px;
  font-variant-numeric: numerator;
  white-space: nowrap;
}

.plant-price {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  text-align: end;
  font-variant-numeric: numerator;
  white-space: nowrap;
}

/* Care timeline: the most recent records, newest first. */
.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
}

.timeline li {
  position: relative;
  padding: 0 0 var(--sp-md) var(--sp-lg);
  border-left: 1px solid var(--hairline);
  margin: 0;
}

.timeline li:last-child {
  padding-bottom: 0;
  border-left-color: transparent;
}

.timeline li::before {
  content: "";
  position: absolute;
  inset-inline-start: -4px;
  inset-block-start: 6px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--primary);
  border: 2px solid var(--canvas);
}

.timeline-type {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink);
}

.timeline-meta {
  font-size: 12px;
  line-height: 1.3;
  letter-spacing: -0.12px;
  color: var(--ink-muted-48);
  margin-top: 2px;
}

.timeline-note {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  margin-top: 5px;
  overflow-wrap: anywhere;
}

/* Care-type breakdown: label, count, and a proportional bar. */
.breakdown {
  display: grid;
  gap: var(--sp-sm);
}

.breakdown-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--sp-sm) var(--sp-md);
}

.breakdown-label {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.breakdown-count {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink);
  font-variant-numeric: numerator;
}

.breakdown-bar {
  grid-column: 1 / -1;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--divider-soft);
  overflow: hidden;
}

.breakdown-fill {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--primary);
}

/* Quick actions: the doors into the working screens. They close the page on
   a near-black band, so the rhythm reads dark -> light -> dark the way
   apple.com alternates its tiles. */
.closer {
  background: var(--tile-1);
  color: var(--on-dark);
  padding: var(--sp-section) var(--sp-xl);
}

.closer-inner {
  max-width: var(--max-content);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  gap: var(--sp-xxl);
  align-items: center;
}

.closer h2 {
  font-size: 40px;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.374px;
  color: var(--on-dark);
  margin: 0;
}

.closer p {
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--body-muted);
  margin: var(--sp-sm) 0 0;
  max-width: 34em;
}

.quick-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-xs);
}

.quick {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-sm);
  padding: 14px var(--sp-md);
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: var(--r-pill);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--on-dark);
  text-decoration: none;
  min-height: 44px;
  position: relative;
}

.quick::after {
  content: "";
  position: absolute;
  inset-block: -6px;
  inset-inline: -4px;
}

.quick:hover {
  text-decoration: none;
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.24);
}

.quick-arrow {
  color: var(--primary-on-dark);
  font-weight: 400;
}

.panel-empty {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-48);
  text-align: center;
  padding: var(--sp-lg) 0;
}

/* ---------- Board error state ----------
   The dashboard refuses to show numbers it could not read. This is not an
   empty state: an empty state would claim the campus has no plants, which
   is a different and wrong statement. The block carries an icon and a
   heading as well as colour, so the state never depends on hue alone. */

.panel-error {
  border: 1px solid rgba(184, 80, 0, 0.28);
  border-inline-start: 3px solid var(--warning);
  border-radius: var(--r-lg);
  background: var(--canvas);
  padding: var(--sp-xl);
}

.panel-error h2 {
  font-size: 24px;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.374px;
  color: var(--ink);
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-xs);
}

.panel-error h2::before {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--warning);
  flex: none;
}

.panel-error p {
  font-size: 17px;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--ink-muted-80);
  margin: var(--sp-sm) 0 0;
  max-width: 62ch;
}

.panel-error-next {
  font-size: 14px !important;
  line-height: 1.43 !important;
  color: var(--ink-muted-48) !important;
}

.panel-error-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm);
  margin-top: var(--sp-lg);
}

.panel-error-actions .btn-secondary {
  background: var(--canvas);
  color: var(--primary);
  border-color: var(--primary);
}

.panel-error-actions .btn-secondary:hover {
  background: var(--canvas-parchment);
}

/* ---------- Hero: the Apple homepage grammar ----------
   The photograph is the floor and the type floats on it. Copy sits in the
   upper band, the image fills the lower two thirds, and a scrim keeps the
   type readable where it crosses the horizon of the picture. */

.hero {
  position: relative;
  background: var(--surface-black);
  color: var(--on-dark);
  text-align: center;
  overflow: hidden;
  isolation: isolate;
}

/* The image is the hero's background plane: it covers the whole section and
   sits behind everything, so the headline reads as floating over it. */
.hero-media {
  position: absolute;
  inset: 0;
  z-index: -2;
  background: var(--surface-black);
}

.hero-media picture {
  display: block;
  width: 100%;
  height: 100%;
}

.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The source is a 4979x1139 ultra-wide frame; bias the crop toward the
     tree line and the building so the type lands on sky and dark canopy. */
  object-position: center 58%;
  border-radius: 0;
  box-shadow: none;
}

/* A vertical scrim. The headline lands around 30% of the hero, so the
   dense part has to hold through there, then release the photograph's own
   light in the lower half. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    rgba(8, 8, 10, 0.9) 0%,
    rgba(8, 8, 10, 0.86) 22%,
    rgba(8, 8, 10, 0.72) 42%,
    rgba(8, 8, 10, 0.44) 62%,
    rgba(8, 8, 10, 0.2) 82%,
    rgba(8, 8, 10, 0.1) 100%
  );
}

.hero-copy {
  position: relative;
  max-width: var(--max-content);
  margin: 0 auto;
  padding: var(--sp-section) var(--sp-xl) 0;
  /* Push the copy into the upper band so the photograph owns the lower half,
     and let the last element's spacing do the rest. */
  min-height: min(78vh, 720px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: clamp(180px, 26vh, 300px);
}

.hero .eyebrow {
  font-size: 21px;
  font-weight: 600;
  line-height: 1.19;
  letter-spacing: 0.231px;
  color: var(--primary-on-dark);
  margin: 0 0 var(--sp-sm);
}

.hero h1 {
  font-family: var(--font-display);
  font-size: 96px;
  font-weight: 600;
  /* The signature Apple cadence: the bigger the type, the tighter it sets. */
  line-height: 1.05;
  letter-spacing: -0.5px;
  color: var(--on-dark);
  margin: 0;
  /* Chinese headlines must be allowed to break between characters, or a
     long system name forces the whole line to shrink. */
  text-wrap: balance;
  word-break: keep-all;
}

.hero .lead {
  font-size: 28px;
  font-weight: 400;
  line-height: 1.29;
  letter-spacing: 0.196px;
  color: var(--body-muted);
  margin: var(--sp-md) auto 0;
  max-width: 22em;
  text-wrap: balance;
}

.hero .btn-row {
  justify-content: center;
  margin-top: var(--sp-xl);
  gap: var(--sp-md);
}

/* On dark, the primary pill becomes the light-on-dark link colour and the
   secondary becomes the quiet outline, mirroring apple.com. */
.hero .btn {
  background: var(--primary-on-dark);
  border-color: var(--primary-on-dark);
  color: var(--tile-1);
  padding: 14px 28px;
  font-size: 17px;
}

.hero .btn:hover {
  background: #4aa8ff;
  border-color: #4aa8ff;
}

.hero .btn-secondary {
  /* The photo behind this button includes pale building faces, so the ghost
     pill carries its own dark fill instead of trusting the backdrop. */
  background: rgba(8, 8, 10, 0.55);
  color: #7fc4ff;
  border-color: rgba(127, 196, 255, 0.9);
}

.hero .btn-secondary:hover {
  background: rgba(8, 8, 10, 0.78);
  color: #a8d8ff;
  border-color: #a8d8ff;
}

/* The caption states where the numbers below came from, so it has to stay
   readable. It sits directly on the photograph with no other treatment, and
   the photo's brightest pixel measures 1.00:1 against the old grey, so the
   fix is a backing plus --on-media, not a lighter grey: pure white on that
   same pixel is still only 1.33:1, which no foreground can survive.

   The scrim is a flat wash rather than a gradient because this band is
   short and the text sits on one line; a gradient would go thin exactly
   where the text is. It spans the caption block, not the whole hero, so
   the photograph's lower edge keeps its own light. */
.hero-caption {
  position: relative;
  max-width: var(--max-content);
  margin: 0 auto;
  padding: var(--sp-md) var(--sp-xl) var(--sp-lg);
  font-size: 12px;
  line-height: 1.43;
  letter-spacing: -0.12px;
  /* Text on photography needs the media neutral, not the dark-surface one:
     on a scrimmed backdrop this clears AA at 12px. */
  color: var(--on-media);
  text-align: start;
}

.hero-caption::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Painted behind the text without taking the caption out of flow. */
  z-index: -1;
  background: var(--scrim-caption);
}

/* ---------- Campus band ----------
   A 2x2 grid of real campus photography between the data board and the
   dark closer. It gives the page a place to breathe and, more usefully,
   shows the actual place the system manages. */

.campus {
  padding: var(--sp-section) var(--sp-xl);
  background: var(--canvas);
}

.campus-inner {
  max-width: var(--max-content);
  margin: 0 auto;
}

.campus-head {
  margin-bottom: var(--sp-xl);
}

.campus-head h2 {
  font-size: 40px;
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.374px;
  margin: 0;
  color: var(--ink);
}

.campus-head p {
  font-size: 17px;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--ink-muted-80);
  margin: var(--sp-sm) 0 0;
  max-width: 52ch;
}

.campus-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-sm);
}

.campus-figure {
  margin: 0;
  position: relative;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--canvas-parchment);
}

/* <picture> is inline by default, which would leave a baseline gap under
   the image. Block it so the figure has no stray space at the bottom. */
.campus-figure picture {
  display: block;
}

.campus-figure img {
  display: block;
  width: 100%;
  height: clamp(180px, 20vw, 300px);
  object-fit: cover;
  border-radius: 0;
  box-shadow: none;
}

.campus-figure figcaption {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  padding: var(--sp-md) var(--sp-lg);
  /* The scrim never fades to nothing: a photo can be white sky or pale
     brick, and the caption has to stay readable on both. */
  background: var(--scrim-media);
  color: var(--on-media);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
}

/* ---------- Store utility cards: white, 1px hairline, 18px radius ---------- */

.card {
  background: var(--canvas);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: var(--sp-lg);
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--sp-lg);
}

.card-grid-spaced {
  margin-top: var(--sp-xl);
}

.card h3 {
  font-size: 17px;
  font-weight: 600;
  line-height: 1.24;
  letter-spacing: -0.374px;
  margin: 0 0 var(--sp-xxs);
}

.card p {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  margin: 0 0 var(--sp-xs);
}

/* ---------- Forms ---------- */

.field {
  margin: 0 0 var(--sp-md);
}

.field label,
.field-label {
  display: block;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  margin-bottom: var(--sp-xs);
}

.field input[type="text"],
.field input[type="number"],
.field input[type="date"],
.field select,
.field textarea {
  width: 100%;
  font-family: var(--font-text);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--body);
  background: var(--canvas);
  border: 1px solid var(--ring);
  border-radius: var(--r-sm);
  padding: 11px 14px;
  transition: border-color 0.2s ease, outline-color 0.2s ease;
}

.field textarea {
  border-radius: var(--r-sm);
  line-height: 1.47;
  resize: vertical;
}

.field select {
  appearance: none;
  -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-muted-48) 50%),
    linear-gradient(135deg, var(--ink-muted-48) 50%, transparent 50%);
  background-position: calc(100% - 20px) calc(50% + 1px), calc(100% - 15px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 40px;
  cursor: pointer;
}

.field input[type="text"]:focus,
.field select:focus,
.field textarea:focus {
  outline: 2px solid var(--primary-focus);
  outline-offset: 0;
  border-color: var(--primary-focus);
}

.field input[readonly] {
  background: var(--canvas-parchment);
  color: var(--ink-muted-48);
}

.field-hint {
  display: block;
  font-size: 12px;
  line-height: 1.0;
  letter-spacing: -0.12px;
  color: var(--ink-muted-48);
  margin-top: var(--sp-xs);
}

/* Field error: message plus an icon rule, never colour alone. */
.field-error {
  display: flex;
  align-items: baseline;
  gap: var(--sp-xs);
  font-size: 12px;
  line-height: 1.3;
  letter-spacing: -0.12px;
  color: var(--danger);
  margin-top: var(--sp-xs);
}

.field-error::before {
  content: "!";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--danger);
  color: var(--on-primary);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  transform: translateY(2px);
}

.field [aria-invalid="true"] {
  border-color: var(--danger);
  background: rgba(200, 16, 46, 0.03);
}

.radio-row {
  display: flex;
  gap: var(--sp-md);
  align-items: center;
  flex-wrap: wrap;
  min-height: 44px;
}

.radio-row label {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  font-size: 17px;
  line-height: 1.47;
  letter-spacing: -0.374px;
  color: var(--body);
  cursor: pointer;
}

.radio-row input[type="radio"],
.radio-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  accent-color: var(--primary);
  cursor: pointer;
}

.form-card {
  max-width: 640px;
  margin: 0 auto;
}

.form-actions {
  display: flex;
  gap: var(--sp-sm);
  flex-wrap: wrap;
  margin-top: var(--sp-lg);
}

/* ---------- Search input: pill, 44px ---------- */

/* Search: the pill carries a clear affordance and a live result count so a
   filtered view never leaves the user guessing how much they are seeing. */
.search-field {
  position: relative;
  display: flex;
  align-items: center;
  flex: 1 1 auto;
  min-width: 0;
}

.search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--ink-muted-48);
  cursor: pointer;
  /* The glyph is a cross drawn with two layers of the same circle. */
  -webkit-mask-image: radial-gradient(circle, transparent 42%, #000 44%),
    linear-gradient(45deg, transparent 44%, #000 46%, #000 54%, transparent 56%),
    linear-gradient(-45deg, transparent 44%, #000 46%, #000 54%, transparent 56%);
  -webkit-mask-composite: source-in;
  mask-image: radial-gradient(circle, transparent 42%, #000 44%),
    linear-gradient(45deg, transparent 44%, #000 46%, #000 54%, transparent 56%),
    linear-gradient(-45deg, transparent 44%, #000 46%, #000 54%, transparent 56%);
  mask-composite: intersect;
}

.search-input::-webkit-search-cancel-button:hover {
  background: var(--ink);
}

.result-count {
  font-size: 14px;
  font-weight: 400;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  margin: 0 0 var(--sp-md);
}

.result-count strong {
  font-weight: 600;
  font-variant-numeric: numerator;
  color: var(--ink);
}

.search-row {
  display: flex;
  gap: var(--sp-sm);
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: var(--sp-lg);
}

.search-input {
  flex: 1 1 260px;
  min-width: 0;
  max-width: 420px;
  font-family: var(--font-text);
  font-size: 17px;
  font-weight: 400;
  letter-spacing: -0.374px;
  color: var(--body);
  background: var(--canvas);
  border: 1px solid var(--ring);
  border-radius: var(--r-pill);
  padding: 12px 20px;
  height: 44px;
}

.search-input:focus {
  outline: 2px solid var(--primary-focus);
  outline-offset: 0;
  border-color: var(--primary-focus);
}

.search-input::placeholder {
  color: var(--ink-muted-48);
}

/* ---------- Data table: hairlines only, no shadow ---------- */

.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

table.data {
  width: 100%;
  min-width: 720px;
  border-collapse: collapse;
  font-size: 17px;
  line-height: 1.47;
  letter-spacing: -0.374px;
  background: var(--canvas);
}

table.data thead th {
  font-family: var(--font-text);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  text-align: left;
  color: var(--ink-muted-80);
  padding: var(--sp-sm) var(--sp-md);
  border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
}

table.data tbody td {
  padding: var(--sp-md);
  border-bottom: 1px solid var(--divider-soft);
  vertical-align: top;
  color: var(--body);
}

/* Row hover only where a pointer exists. Never the sole carrier of meaning. */
@media (hover: hover) and (pointer: fine) {
  table.data tbody tr:hover {
    background: var(--canvas-parchment);
  }

  table.data tbody tr:hover .cell-muted {
    color: var(--ink-muted-80);
  }
}

table.data tbody tr:last-child td {
  border-bottom: none;
}

table.data .cell-muted {
  color: var(--ink-muted-80);
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
}

table.data .cell-num {
  font-variant-numeric: numerator;
  white-space: nowrap;
}

table.data .actions {
  white-space: nowrap;
}

.empty-state {
  text-align: center;
  padding: var(--sp-xxl) var(--sp-lg);
  color: var(--ink-muted-48);
  font-size: 17px;
  line-height: 1.47;
  letter-spacing: -0.374px;
}

/* Empty state that teaches the space and offers the way out. */
.empty-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-xs);
  padding: var(--sp-xxl) var(--sp-lg);
  text-align: center;
}

.empty-card .empty-state {
  padding: 0;
  color: var(--ink);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.19;
  letter-spacing: 0.231px;
  margin: 0;
}

.empty-card .empty-hint {
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
  margin: 0 0 var(--sp-sm);
  max-width: 44ch;
}

.empty-card .btn {
  display: inline-block;
  width: auto;
}

/* ---------- Footer: parchment, dense link columns ---------- */

.site-footer {
  background: var(--canvas-parchment);
  color: var(--ink-muted-80);
  padding: 64px var(--sp-xl);
  font-size: 12px;
  line-height: 1;
  letter-spacing: -0.12px;
}

.footer-inner {
  max-width: var(--max-content);
  margin: 0 auto;
}

.footer-columns {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--sp-xl) var(--sp-lg);
  margin-bottom: var(--sp-xl);
}

/* The footer is a landmark region, so it carries its own heading for
   structure; the column titles carry the visual weight. */
.visuallyhidden-footer {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.footer-columns h3 {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.29;
  letter-spacing: -0.224px;
  color: var(--ink);
  margin: 0 0 var(--sp-xs);
}

.footer-links {
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer-links li {
  margin-bottom: 0;
}

.footer-links a {
  display: block;
  font-size: 17px;
  font-weight: 400;
  line-height: 2.41;
  letter-spacing: 0;
  color: var(--ink-muted-80);
}

.footer-links a:hover {
  color: var(--primary);
  text-decoration: none;
}

.footer-legal {
  border-top: 1px solid var(--hairline);
  padding-top: var(--sp-md);
  font-size: 12px;
  line-height: 1;
  letter-spacing: -0.12px;
  color: var(--ink-muted-48);
}

.footer-legal p {
  margin: 0;
}

/* ---------- Utility ---------- */

.page-reading {
  max-width: var(--max-reading);
  margin: 0 auto;
  padding: var(--sp-section) var(--sp-xl);
}

.display-md {
  font-size: 34px;
  font-weight: 600;
  line-height: 1.47;
  letter-spacing: -0.374px;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Responsive: 1440 / 1068 / 833 / 640 / 419 ---------- */

@media (max-width: 1068px) {
  .tile .hero-display {
    font-size: 40px;
  }

  .tile .display-lg {
    font-size: 34px;
  }

  .tile .lead {
    font-size: 24px;
  }

  .page-head h1 {
    font-size: 28px;
  }



  /* Dashboard: metrics go 2x2, then the board collapses to one column. */
  .stat-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .dash-body {
    grid-template-columns: minmax(0, 1fr);
  }

  .closer-inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-xl);
  }

  .hero h1 {
    font-size: 64px;
  }

  .hero .lead {
    font-size: 24px;
  }

  .campus-head h2 {
    font-size: 28px;
  }
}

@media (max-width: 833px) {
  :root {
    --sp-section: 64px;
  }

  .global-nav {
    justify-content: space-between;
    gap: var(--sp-sm);
    padding: 0 var(--sp-md);
  }

  .sub-nav-inner {
    padding: 0 var(--sp-lg);
  }

  .hero-copy .lead {
    max-width: 60ch;
  }

  .hero-copy h1 {
    font-size: 64px;
  }

  .hero-copy {
    min-height: 0;
    padding-bottom: clamp(140px, 28vh, 240px);
  }

}

@media (max-width: 640px) {
  .tile {
    padding: 48px var(--sp-lg);
  }

  .tile .hero-display {
    font-size: 34px;
  }

  .page-reading {
    padding: 48px var(--sp-lg);
  }


  .page-head {
    padding: var(--sp-lg) var(--sp-lg) var(--sp-md);
  }

  .page-work {
    padding: var(--sp-lg);
  }

  .page-head-inner {
    align-items: stretch;
    flex-direction: column;
    gap: var(--sp-md);
  }

  .page-head .search-row {
    width: 100%;
    flex-wrap: wrap;
  }

  .page-head .search-input {
    flex: 1 1 100%;
    min-width: 0;
  }

  .page-head .search-row .btn {
    width: 100%;
  }

  .page-head h1 {
    font-size: 24px;
  }

  .dash {
    padding: var(--sp-md) var(--sp-md) 48px;
  }

  .dash-head {
    margin-bottom: var(--sp-lg);
  }

  .dash-head h2 {
    font-size: 21px;
  }

  .dash-clock {
    text-align: start;
  }

  /* A three-column row cannot survive a phone: the meter drops below the
     name so nothing is crushed into an unreadable sliver. */
  .plant-row {
    grid-template-columns: minmax(0, 1fr) auto;
    row-gap: var(--sp-xs);
  }

  .plant-row .meter {
    grid-column: 1 / -1;
  }

  .stat-value {
    font-size: 34px;
  }

  .quick-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Hero scales with the viewport rather than snapping at each breakpoint,
     so the headline keeps its proportion to the screen it sits on. */
  .hero-copy {
    padding: 48px var(--sp-lg) 0;
    min-height: 0;
    padding-bottom: clamp(120px, 34vh, 200px);
  }

  .hero h1 {
    font-size: 40px;
    line-height: 1.1;
  }

  .hero .eyebrow {
    font-size: 17px;
  }

  .hero .lead {
    font-size: 19px;
    margin-top: var(--sp-sm);
  }

  .hero .btn-row {
    margin-top: var(--sp-lg);
    flex-direction: column;
  }

  .hero .btn {
    width: 100%;
  }

  /* On a phone the copy needs most of the screen, so the photograph is
     biased upward and the scrim stays a little denser behind the type. */
  .hero-media img {
    object-position: center 42%;
  }

  .hero-caption {
    padding: var(--sp-sm) var(--sp-lg) var(--sp-lg);
  }

  .campus {
    padding: 48px var(--sp-md);
  }

  .campus-head h2 {
    font-size: 24px;
  }

  .campus-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .campus-figure img {
    height: clamp(170px, 46vw, 260px);
  }

  /* Metrics stack: the hairline divider becomes a horizontal rule so the
     figure never sits in a one-word-wide column. */
  .stat-row {
    grid-template-columns: minmax(0, 1fr);
    border-bottom: none;
  }

  .stat {
    border-inline-end: none;
    border-bottom: 1px solid var(--hairline);
    padding: var(--sp-lg) 0 var(--sp-lg);
  }

  .stat:last-child {
    border-bottom: none;
  }

  /* Sub-nav collapses to a single scrolling row: category name + CTAs only. */
  .sub-nav-inner {
    flex-direction: column;
    align-items: flex-start;
    height: auto;
    gap: var(--sp-xxs);
    padding-top: var(--sp-xs);
    padding-bottom: var(--sp-xs);
  }

  .sub-nav-links {
    gap: var(--sp-md);
    flex-wrap: nowrap;
    overflow-x: auto;
    width: 100%;
    padding-bottom: 2px;
    -webkit-overflow-scrolling: touch;
  }

  .sub-nav-links a {
    white-space: nowrap;
  }

  .btn {
    width: 100%;
  }

  .btn-row {
    flex-direction: column;
  }
}

@media (max-width: 419px) {
  .tile {
    padding: 48px var(--sp-md);
  }

  .tile .hero-display {
    font-size: 28px;
  }

  .tile .display-lg {
    font-size: 28px;
  }

  .tile .lead {
    font-size: 21px;
  }

  .global-nav {
    font-size: 11px;
  }


  .page-head {
    padding: var(--sp-md) var(--sp-md) var(--sp-sm);
  }

  .page-work {
    padding: var(--sp-md);
  }

  .page-head h1 {
    font-size: 21px;
  }


}

/* ---------- Mobile fixes ---------- */

@media (max-width: 640px) {
  /* .page-head-inner becomes a column on phones, so the desktop flex-basis
     values (which were widths) would be read as heights and inflate the
     header into a huge empty block. Reset them to content height. */
  .page-head-titles,
  .page-head .search-row {
    flex-basis: auto;
  }

  /* The link row no longer fits on one line; let it wrap instead of being
     clipped mid-item by a horizontal scroll. */
  .global-nav {
    height: auto;
    flex-direction: column;
    gap: 4px;
    padding: 6px var(--sp-md);
    white-space: normal;
    overflow-x: visible;
  }

  .global-nav nav {
    flex-wrap: wrap;
    justify-content: center;
    row-gap: 4px;
  }
}

/* Homepage: the panel that used to repeat the dashboard charts is now a
   pointer to the board and the map. */
.dash-cta-text {
  margin: 0;
  font-size: 15px;
  line-height: 1.5;
  letter-spacing: -0.224px;
  color: var(--ink-muted-80);
}

/* ---------- Pagination Bar ---------- */
.pagination-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-md) var(--sp-lg);
  border-top: 1px solid var(--hairline);
  flex-wrap: wrap;
  gap: var(--sp-md);
  background: var(--canvas);
}

.pagination-info {
  font-size: 13px;
  color: var(--ink-muted-48);
  font-variant-numeric: tabular-nums;
}

.pagination-info strong {
  color: var(--ink);
}

.pagination-btns {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.page-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 32px;
  height: 32px;
  padding: 0 10px;
  font-size: 13px;
  font-weight: 500;
  border-radius: var(--r-sm);
  border: 1px solid var(--hairline);
  background: var(--canvas);
  color: var(--ink);
  text-decoration: none;
  transition: all 0.2s ease;
  font-variant-numeric: tabular-nums;
}

.page-btn:hover:not(.disabled):not(.active) {
  background: var(--canvas-parchment);
  border-color: var(--primary);
  color: var(--primary);
}

.page-btn.active {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
  font-weight: 600;
}

.page-btn.disabled {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* ---------- Image Upload & Preview ---------- */
.file-upload-box {
  display: flex;
  flex-direction: column;
  gap: var(--sp-sm);
}

.file-input-row {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  flex-wrap: wrap;
}

.file-choose-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  background: var(--canvas);
  border: 1px dashed var(--hairline-strong, #86868b);
  border-radius: var(--r-md);
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  cursor: pointer;
  transition: all 0.2s ease;
}

.file-choose-btn:hover {
  border-color: var(--primary);
  color: var(--primary);
  background: rgba(0, 102, 204, 0.04);
}

.file-native-input {
  display: none;
}

.img-preview-box {
  margin-top: var(--sp-xs);
  display: flex;
  align-items: flex-start;
  gap: var(--sp-md);
}

.img-preview-thumb {
  width: 140px;
  height: 100px;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: var(--canvas-parchment);
  display: block;
}

.img-preview-meta {
  font-size: 12px;
  color: var(--ink-muted-48);
  display: flex;
  flex-direction: column;
  gap: 4px;
}
