/* Skill Ready Techs - Complete Stylesheet */
/* ======================================== */
/* Built on top of Pure CSS (loaded before this file on every page - see the
   <link> in each page's <head>) for cross-browser normalization, its grid
   utilities, and baseline table/form resets. Pure was chosen over Foundation
   and Petal because it is unopinionated at the tag level - it doesn't ship
   its own visual "skin" or bundle JS components that fight with this site's
   already-built custom design system (buttons, cards, the tech card, the
   readout panel, etc.), so it can sit underneath as a clean foundation
   without a full markup rewrite. Everything in this file loads AFTER Pure
   and wins the cascade wherever the two disagree. */

:root {
  /* HVACR industry palette. --primary/--primary-light are the deep,
     high-trust navy and are used both as GRADIENT BACKGROUNDS (hero,
     footer, training header, tech card) and as the header/heading ink -
     never as the page/card background, which is why they can stay dark
     even though the rest of the site reads light. Brand accent is the
     "cooling" HVAC blue throughout - --info is a separate, cooler cyan-blue
     used for secondary ("employer side") UI so the two don't collapse into
     one color. --hvac-orange is the "heating" tone, reserved for urgent /
     high-attention CTAs so it doesn't compete with the everyday blue accent. */
  --primary: #1e293b;             /* --color-primary-navy */
  --primary-light: #2c3e58;
  --accent: #0284c7;              /* --color-hvac-blue */
  --accent-hover: #0369a1;
  --accent-soft: #0369a1;   /* readable blue for text sitting on --accent-light chips on this light ground */
  --accent-light: rgba(2, 132, 199, 0.12);
  --hvac-orange: #ea580c;         /* Heating / urgent CTA highlight */
  --hvac-orange-hover: #c2410c;
  --hvac-orange-light: rgba(234, 88, 12, 0.14);
  --success: #10b981;
  --success-light: rgba(16, 185, 129, 0.14);
  --danger: #ef4444;
  --danger-light: rgba(239, 68, 68, 0.12);
  --warning: #f59e0b;
  --warning-soft: #92400e; /* readable amber-brown for text sitting on --warning-light chips on this light ground */
  --warning-light: rgba(245, 158, 11, 0.16);
  --info: #0ea5e9;
  --info-light: rgba(14, 165, 233, 0.13);
  --text: #334155;                /* --color-text-dark */
  --text-light: #4b5b71;
  --text-muted: #6c7d93;
  /* Default page background - a clean, cool-neutral dashboard background
     rather than a tinted "SaaS blue-white", so cards and photography sit on
     a calmer, less templated ground. */
  --bg: #f8fafc;                  /* --color-bg-light */
  --bg-white: #ffffff;
  --bg-elevated: #eef2f6;
  --border: #dce3ea;
  --border-strong: #c3ccd6;
  --shadow-sm: 0 1px 2px 0 rgb(15 23 42 / 0.06);
  --shadow: 0 1px 3px 0 rgb(15 23 42 / 0.08), 0 1px 2px -1px rgb(15 23 42 / 0.06);
  --shadow-lg: 0 10px 15px -3px rgb(15 23 42 / 0.1), 0 4px 6px -4px rgb(15 23 42 / 0.07);
  --shadow-xl: 0 20px 25px -5px rgb(15 23 42 / 0.12), 0 8px 10px -6px rgb(15 23 42 / 0.08);
  /* A tighter, less "bubbly-default-template" radius scale - crisp rather
     than uniformly rounded. */
  --radius: 0.375rem;
  --radius-lg: 0.625rem;
  --radius-xl: 0.875rem;
  /* Signature corner cut used on primary buttons and a few brand touches -
     see .btn-primary / .btn-secondary below - a small deliberate detail
     nodding at instrument-panel switches rather than a stock rounded pill. */
  --notch: 10px;
  --transition: all 0.2s ease-in-out;
  /* Industrial, structured hierarchy: Oswald carries H1-H4 in uppercase
     with tracked letter-spacing (the "read me first" headline tier, styled
     like instrument-panel labeling). Body copy and smaller UI text (H5-H6,
     labels, the brand wordmark) stay on Open Sans - a highly readable
     workhorse sans that keeps long-form dashboard text calm and legible. */
  --font-heading: 'Oswald', 'Segoe UI', Helvetica, Arial, sans-serif;
  --font-body: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-serif: var(--font-heading);
  --font-sans: var(--font-body);
  --font-display: var(--font-sans);
  --font-mono: 'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  color: var(--text);
  background-color: var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* Typography */
/* H1-H4: Oswald headline tier - uppercase, tracked letter-spacing for a
   rugged, structured, instrument-panel look. */
h1, h2, h3, h4 {
  font-family: var(--font-heading);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--primary);
  line-height: 1.2;
}
/* H5-H6: Open Sans, sits with body copy rather than the headline tier. */
h5, h6 { font-family: var(--font-body); font-weight: 700; line-height: 1.15; color: var(--text); letter-spacing: -0.01em; text-transform: none; }
h1 { font-size: 2.75rem; font-weight: 700; line-height: 1.2; }
h2 { font-size: 2rem; font-weight: 500; line-height: 1.3; }
h3 { font-size: 1.5rem; font-weight: 600; }
h4 { font-size: 1.25rem; font-weight: 600; }
p { color: var(--text-light); }
a { text-decoration: none; color: inherit; transition: var(--transition); }
.eyebrow { font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.14em; font-size: 0.75rem; color: var(--accent); font-weight: 600; }

/* Layout */
.container { width: 100%; max-width: 1280px; margin: 0 auto; padding: 0 1.5rem; }
.section { padding: 5rem 0; }
.grid { display: grid; gap: 1.5rem; }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.flex { display: flex; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-4 { gap: 1rem; }
.gap-6 { gap: 1.5rem; }
.gap-8 { gap: 2rem; }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: 0.5rem; padding: 0.75rem 1.5rem; border-radius: var(--radius);
  font-weight: 600; font-size: 0.95rem; cursor: pointer; border: none;
  transition: var(--transition); white-space: nowrap;
}
/* A clipped top-right corner is the site's one recurring shape signature -
   it shows up here, on the logo mark, and on the readout panel - so filled
   buttons read as a deliberately designed instrument rather than a stock
   rounded pill borrowed from a template. Outline/ghost/form controls stay
   plain rounded rectangles so text-heavy UI stays calm. */
.btn-primary, .btn-secondary {
  clip-path: polygon(0 0, calc(100% - var(--notch)) 0, 100% var(--notch), 100% 100%, 0 100%);
  border-radius: 0;
}
.btn-primary {
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-hover) 100%);
  color: white; box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08), 0 4px 10px -4px rgba(2, 132, 199, 0.45);
}
.btn-primary:hover { filter: brightness(1.06); transform: translateY(-1px); box-shadow: 0 8px 20px -6px rgba(2, 132, 199, 0.55); }
.btn-primary:active { transform: translateY(0); filter: brightness(0.97); }
.btn-secondary { background: var(--primary); color: white; }
.btn-secondary:hover { background: var(--primary-light); }
.btn-outline { background: transparent; border: 2px solid var(--border-strong); color: var(--text); }
.btn-outline:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-light); }
.btn-ghost { background: transparent; color: var(--text-light); }
.btn-ghost:hover { background: var(--bg); color: var(--text); }
.btn-success { background: var(--success); color: white; }
.btn-danger { background: var(--danger); color: white; }
.btn-sm { padding: 0.5rem 1rem; font-size: 0.875rem; --notch: 7px; }
.btn-lg { padding: 1rem 2rem; font-size: 1.1rem; --notch: 13px; }
.btn-block { width: 100%; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none !important; }

/* Cards */
.card {
  position: relative;
  background: var(--bg-white); border-radius: var(--radius-lg);
  box-shadow: var(--shadow); border: 1px solid var(--border);
  overflow: hidden; transition: var(--transition);
}
/* A thin accent bar draws in along the top edge on hover instead of the
   card just lifting - a quieter, more considered "this is alive" cue than
   the shadow-and-transform combo every template card uses. */
.card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 100%; height: 3px;
  background: linear-gradient(90deg, var(--accent), var(--info));
  transition: right 0.25s ease;
}
.card:hover::before { right: 0; }
.card:hover { box-shadow: var(--shadow); border-color: var(--border); }
.card-header { padding: 1.5rem; border-bottom: 1px solid var(--border); }
.card-body { padding: 1.5rem; }
.card-footer { padding: 1rem 1.5rem; border-top: 1px solid var(--border); background: var(--bg); }

/* Forms */
.form-group { margin-bottom: 1.25rem; }
.form-label { display: block; font-weight: 600; font-size: 0.875rem; margin-bottom: 0.5rem; color: var(--text); }
.form-input, .form-select, .form-textarea {
  width: 100%; padding: 0.75rem 1rem; border: 2px solid var(--border);
  border-radius: var(--radius); font-size: 0.95rem; transition: var(--transition);
  background: var(--bg-white); color: var(--text);
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(2, 132, 199, 0.14);
}
.form-input::placeholder { color: var(--text-muted); }
.form-hint { font-size: 0.8rem; color: var(--text-muted); margin-top: 0.25rem; }
.form-error { font-size: 0.8rem; color: var(--danger); margin-top: 0.25rem; display: none; }
.form-group.has-error .form-input { border-color: var(--danger); }
.form-group.has-error .form-error { display: block; }

/* Navigation */
.navbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 1000;
  /* High opacity on purpose: this sits over the dark hero on every page that
     has one, and backdrop-filter isn't guaranteed everywhere (older
     browsers, some rendering paths skip it entirely) - at low opacity that
     combination lets hero text ghost through the navbar unblurred. Near-
     opaque keeps the glass look in browsers that do blur it, and stays
     legible/solid everywhere else. */
  background: rgba(255,255,255,0.96); backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  /* A thin navy-to-orange strip stands in for a physical instrument-panel
     top edge - navy (trust) into the "heating" orange, echoing the same two
     brand tones used for the urgent CTA below. */
  border-top: 3px solid var(--primary);
  border-image: linear-gradient(90deg, var(--primary) 0%, var(--hvac-orange) 100%) 1;
  border-bottom: 1px solid var(--border); transition: var(--transition);
}
.navbar.scrolled { box-shadow: var(--shadow); }
.navbar-inner { display: flex; align-items: center; justify-content: space-between; height: 72px; }
.logo {
  display: flex; align-items: center; gap: 0.75rem;
  font-family: var(--font-heading); font-weight: 700; font-size: 1.4rem;
  text-transform: uppercase; letter-spacing: 0.03em; color: var(--primary);
}
/* --- The logo mark ---------------------------------------------------------
   `.logo-icon` is the square slot the brand mark sits in. It ships holding a
   Font Awesome gauge glyph, which needs a frame to read as a mark at all -
   the gradient fill, the clipped top-right corner, the drop shadow.

   A real logo needs none of that, and the frame actively damaged it: the
   clip-path sliced a notch out of the top-right corner of every uploaded
   image, the gradient showed through anything transparent, and `object-fit:
   cover` cropped the sides off anything that wasn't already square.

   So the moment an image goes in, js/app.js adds `has-logo` and all of it
   comes off. What's left is a square box with the artwork laid over it at a
   true 1:1 aspect ratio, sized to fit whole (`contain`) rather than cropped
   to fill. Keeping the slot square regardless of the source file's
   proportions is what makes the mark line up with the wordmark beside it on
   every page, at every breakpoint. */
.logo-image {
  width: 100%; height: 100%; display: block;
  object-fit: contain; object-position: center;
  border-radius: 0; background: transparent;
}
.logo-icon {
  width: 40px; height: 40px; aspect-ratio: 1 / 1; flex-shrink: 0;
  background: linear-gradient(135deg, var(--primary), var(--accent));
  clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%);
  display: flex; align-items: center; justify-content: center; color: white; font-size: 1.25rem;
  box-shadow: 0 3px 8px -2px rgba(2, 132, 199, 0.5);
  overflow: visible;
}
.logo-icon.has-logo {
  background: none;
  clip-path: none;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
  overflow: visible;
}
/* On the dark navy surfaces - the footer on every page, and the sign-in
   panel on login.html - a transparent logo with dark ink in it would simply
   disappear into the background. A white disc sits BEHIND the artwork (the
   image is still drawn at full size on top of it, so nothing is cut off or
   covered) to give it something to read against. The default seal is round,
   so the disc reads as part of the mark. */
.footer .logo-icon.has-logo,
.auth-sidebar .logo-icon.has-logo {
  background: #ffffff;
  border-radius: 50%;
  padding: 2px;
}
/* Nav links read like instrument labels - small mono caps with tracking -
   rather than the plain-prose nav text most template navbars ship with. */
.nav-links { display: flex; align-items: center; gap: 1.75rem; list-style: none; }
.nav-links a {
  font-family: var(--font-mono); font-weight: 500; font-size: 0.78rem;
  text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text-light); position: relative;
}
.nav-links a:hover, .nav-links a.active { color: var(--accent); }
.nav-links a::after {
  content: ''; position: absolute; left: 50%; right: 50%; bottom: -6px; height: 2px;
  background: var(--accent); transition: left 0.18s ease-in-out, right 0.18s ease-in-out;
  border-radius: 2px;
}
.nav-links a:hover::after, .nav-links a.active::after { left: 0; right: 0; }

/* --- Signed-in header: one menu, the hamburger ----------------------------
   For a signed-in Technician or Employer the header carries exactly one
   menu. The account caret dropdown that used to sit beside the avatar is
   gone, and Edit Profile / Log Out now live in the hamburger panel with the
   page navigation (App.buildNavMenu() in js/app.js adds both, and adds
   `nav-collapsed` to <body>).

   That only works if the hamburger is reachable at every width, so these
   rules give `.nav-links` the same dropdown-panel treatment on a desktop
   that it has always had on a phone, rather than leaving a signed-in
   desktop visitor with no Profile or Log Out control in the header at all.
   They are scoped to body.nav-collapsed, so a signed-out visitor still gets
   the ordinary inline nav row at desktop widths, unchanged.

   Specificity note: these intentionally outrank the width-scoped
   `.nav-links` rules further down the file, so the panel behaves the same
   above and below the 768px breakpoint. */
body.nav-collapsed .nav-links {
  display: none;
  position: absolute;
  top: 100%; left: 0; right: 0;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  background: var(--bg-white);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  padding: 0.5rem 0;
  max-height: calc(100vh - 72px);
  overflow-y: auto;
}
body.nav-collapsed .nav-links.active { display: flex; }
body.nav-collapsed .nav-links li { width: 100%; }
body.nav-collapsed .nav-links a { display: block; padding: 0.9rem 1.5rem; font-size: 0.85rem; }
body.nav-collapsed .nav-links a::after { display: none; }
body.nav-collapsed .mobile-menu-btn { display: block; }

/* The account block at the foot of that panel. Separated from the page
   links above it so "Log Out" never reads as just another destination. */
.nav-account-heading {
  margin-top: 0.4rem;
  padding: 0.75rem 1.5rem 0.3rem;
  border-top: 1px solid var(--border);
  font-family: var(--font-mono); font-weight: 500; font-size: 0.68rem;
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-muted);
}
.nav-account-item a { display: flex !important; align-items: center; gap: 0.65rem; }
.nav-account-item a i { width: 16px; text-align: center; }

.nav-actions { display: flex; align-items: center; gap: 1rem; }
/* The header's own CTA (Log In / Get Started) is the one place the "urgent
   CTA" heating-orange gets used, so it reads as the one clear next action
   in the header without recoloring every primary button site-wide. */
.nav-actions .btn-primary {
  background: linear-gradient(135deg, var(--hvac-orange) 0%, var(--hvac-orange-hover) 100%);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08), 0 4px 10px -4px rgba(234, 88, 12, 0.5);
}
.nav-actions .btn-primary:hover { box-shadow: 0 8px 20px -6px rgba(234, 88, 12, 0.6); }
.user-menu { display: flex; align-items: center; gap: 0.5rem; flex-wrap: nowrap; }

/* Signed-in header controls - markup built by App.buildUserMenu() in
   js/app.js. The notifications bell mounts itself into this same container
   at whatever position it lands, so the running order of the three controls
   is pinned here instead of by DOM position: avatar/dashboard button, then
   bell, then the account caret. */
.user-menu .user-home-btn { order: 1; }
.user-menu .notif-bell-wrap { order: 2; }

/* The avatar button goes to the person's dashboard, not their profile
   settings, so it is styled to read as a destination rather than a bare
   avatar: a raised pill carrying the account name and an explicit
   "Dashboard" tag. */
.user-home-btn {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.35rem 0.6rem 0.35rem 0.4rem;
  border: 1px solid var(--border); border-radius: 9999px;
  background: var(--bg-white);
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.user-home-btn:hover, .user-home-btn.active {
  border-color: var(--accent);
  box-shadow: 0 2px 10px -4px rgba(15, 23, 42, 0.28);
}
.user-home-btn .user-avatar {
  width: 32px; height: 32px; border-radius: 50%; color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 0.875rem; flex-shrink: 0;
  background-size: cover; background-position: center;
}
.user-home-btn .user-name {
  font-weight: 600; max-width: 150px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.user-home-tag {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.15rem 0.5rem; border-radius: 9999px;
  background: var(--accent-light); color: var(--accent);
  font-size: 0.68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.03em; white-space: nowrap;
}
.user-home-tag i { font-size: 0.7rem; }

/* Notifications bell + dropdown - see js/notifications.js. Mounted into
   every .user-menu on the page for a signed-in Technician or Employer. */
.notif-bell-wrap { position: relative; display: flex; align-items: center; }
.notif-bell { position: relative; padding: 0.5rem 0.65rem !important; }
.notif-badge {
  position: absolute; top: 2px; right: 2px; min-width: 16px; height: 16px; padding: 0 3px;
  border-radius: 9999px; background: var(--danger, #dc2626); color: #fff; font-size: 0.65rem;
  font-weight: 700; line-height: 16px; text-align: center;
}
.notif-panel {
  position: absolute; top: calc(100% + 10px); right: 0; width: 320px; max-width: 88vw;
  max-height: 420px; overflow-y: auto; background: var(--bg-white); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 12px 32px -8px rgba(15, 23, 42, 0.25); z-index: 200;
}
/* Below ~480px the bell doesn't sit flush with the screen's right edge (the
   user menu/avatar and, on most pages, the hamburger button both take up
   space to its right), so anchoring the panel with `right: 0` off the bell
   itself runs it off the LEFT edge of the screen at common phone widths
   (390px, 375px, 360px and more all clipped part of the panel before this).
   Switching to viewport-fixed positioning here guarantees the whole panel
   - header, list, and its edges - stays fully on-screen regardless of
   where the bell happens to sit in the header. */
@media (max-width: 480px) {
  .notif-panel {
    position: fixed;
    top: 64px;
    left: 0.75rem;
    right: 0.75rem;
    width: auto;
    max-width: none;
    max-height: min(420px, calc(100vh - 64px - 5rem));
  }
}
.notif-panel-header {
  display: flex; justify-content: space-between; align-items: center; padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--bg-white);
}
.notif-mark-read { background: none; border: none; color: var(--accent); font-size: 0.75rem; font-weight: 600; cursor: pointer; }
.notif-empty { padding: 1.5rem 1rem; text-align: center; color: var(--text-muted); font-size: 0.85rem; }
/* Separator between the "No new messages." banner and the already-read
   notification history kept below it (see renderList in js/notifications.js). */
.notif-panel-divider {
  padding: 0.45rem 1rem; font-family: var(--font-mono); font-size: 0.65rem;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-muted);
  background: var(--bg); border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.notif-item {
  display: block; width: 100%; padding: 0.75rem 1rem; border-bottom: 1px solid var(--border);
  text-decoration: none; color: var(--text); background: none; border-left: none; border-right: none; border-top: none;
  text-align: left; font: inherit; cursor: pointer;
}
.notif-item:last-child { border-bottom: none; }
.notif-item:hover { background: var(--bg); }
.notif-item.unread { background: var(--accent-light); }
.notif-item-title { font-weight: 600; font-size: 0.875rem; }
.notif-item-body { font-size: 0.8rem; color: var(--text-light); margin-top: 0.2rem; }
.notif-item-time { font-size: 0.7rem; color: var(--text-muted); margin-top: 0.35rem; }

/* Small on/off switch reused for notification preferences and similar
   per-account toggles. Markup: <label class="switch"><input type="checkbox">
   <span class="switch-track"></span></label> */
.switch { position: relative; display: inline-block; width: 42px; height: 24px; flex-shrink: 0; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch-track { position: absolute; cursor: pointer; inset: 0; background: var(--border); border-radius: 24px; transition: 0.2s; }
.switch-track::before { content: ''; position: absolute; height: 18px; width: 18px; left: 3px; bottom: 3px; background: #fff; border-radius: 50%; transition: 0.2s; }
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::before { transform: translateX(18px); }
.mobile-menu-btn { display: none; background: none; border: none; font-size: 1.5rem; cursor: pointer; color: var(--text); }

/* Hero */
.hero {
  position: relative; padding: 9rem 0 5rem; overflow: hidden;
  background: linear-gradient(160deg, var(--primary) 0%, var(--primary-light) 60%, #1e3a5f 100%);
}
.hero::before {
  content: ''; position: absolute; inset: 0; opacity: 0.5;
  background-image:
    linear-gradient(rgba(255,255,255,0.045) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.045) 1px, transparent 1px);
  background-size: 42px 42px;
  mask-image: radial-gradient(circle at 30% 30%, black 0%, transparent 70%);
}
.hero::after {
  content: ''; position: absolute; top: -20%; right: -10%; width: 55%; height: 90%;
  background: radial-gradient(circle, rgba(2, 132, 199, 0.35) 0%, transparent 70%);
  pointer-events: none;
}
.hero-inner { position: relative; z-index: 1; display: grid; grid-template-columns: 1.1fr 0.9fr; gap: 3.5rem; align-items: center; }
.hero-content { position: relative; z-index: 1; text-align: left; max-width: 620px; }
.hero .eyebrow { color: #93c5fd; }
.hero h1 { color: white; margin: 1rem 0 1.5rem; font-size: 3rem; }
.hero h1 span { color: #60a5fa; position: relative; }
.hero p { color: rgba(255,255,255,0.92); font-size: 1.15rem; margin-bottom: 2.25rem; max-width: 540px; }
.hero-buttons { display: flex; gap: 1rem; flex-wrap: wrap; }

/* Readout panel - signature hero element, styled like a technician's gauge cluster */
.readout-panel {
  position: relative; background: rgba(13, 27, 42, 0.55); border: 1px solid rgba(255,255,255,0.12);
  border-radius: var(--radius-xl); padding: 1.75rem; backdrop-filter: blur(6px);
  box-shadow: var(--shadow-xl);
}
.readout-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.25rem; padding-bottom: 1rem; border-bottom: 1px dashed rgba(255,255,255,0.15); }
.readout-header span { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(255,255,255,0.75); }
.readout-live { display: flex; align-items: center; gap: 0.4rem; font-family: var(--font-mono); font-size: 0.7rem; color: #7fe3a3; }
.readout-live-dot { width: 7px; height: 7px; border-radius: 50%; background: #7fe3a3; box-shadow: 0 0 0 0 rgba(127,227,163,0.7); animation: pulseDot 2s infinite; }
@keyframes pulseDot { 0% { box-shadow: 0 0 0 0 rgba(127,227,163,0.5); } 70% { box-shadow: 0 0 0 8px rgba(127,227,163,0); } 100% { box-shadow: 0 0 0 0 rgba(127,227,163,0); } }
.readout-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.85rem; margin-bottom: 1.25rem; }
.readout-cell { background: rgba(255,255,255,0.045); border: 1px solid rgba(255,255,255,0.09); border-radius: var(--radius); padding: 0.9rem 1rem; }
/* Skills / Certifications / EPA 608 / Availability - readable sans-serif
   labeling in a calm, neutral tone rather than the mono "gauge" numerals
   used elsewhere in this panel, since these are profile facts an employer
   reads as text, not live telemetry. */
.readout-cell-label {
  font-family: var(--font-body); font-size: 0.7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.07em;
  color: rgba(226, 232, 240, 0.6); margin-bottom: 0.4rem;
}
.readout-cell-value {
  font-family: var(--font-body); font-size: 0.95rem; font-weight: 600;
  color: #eef2f7; line-height: 1.35;
}
/* Status-style values (EPA 608, Availability) get a quiet pill rather than
   neon full-text color - the checkmark carries the "verified/good" signal
   so the label itself can stay neutral and easy to read. */
.readout-cell-value.status {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: rgba(226, 232, 240, 0.1); border: 1px solid rgba(226, 232, 240, 0.16);
  padding: 0.3rem 0.6rem; border-radius: 4px; font-size: 0.85rem;
}
.readout-cell-value.status i { color: #4ade80; font-size: 0.8rem; }
.readout-note { font-size: 0.85rem; color: rgba(255,255,255,0.8); display: flex; gap: 0.6rem; align-items: flex-start; }
.readout-note i { color: #7fe3a3; margin-top: 0.15rem; }

/* Growth strip - replaces raw vanity metrics with momentum messaging */
.growth-strip { display: flex; justify-content: center; gap: 2.5rem; margin-top: 3.5rem; flex-wrap: wrap; }
.growth-item { display: flex; align-items: center; gap: 0.75rem; color: rgba(255,255,255,0.93); font-size: 0.95rem; max-width: 260px; }
.growth-item i { color: #60a5fa; font-size: 1.1rem; }

/* Section Headers */
.section-header { text-align: center; max-width: 600px; margin: 0 auto 3rem; }
.section-header h2 { margin-bottom: 1rem; }
.section-header p { font-size: 1.125rem; }
.badge {
  display: inline-flex; align-items: center; gap: 0.5rem;
  padding: 0.5rem 1rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 600;
}
.badge-accent { background: var(--accent-light); color: var(--accent-soft); }
.badge-success { background: var(--success-light); color: var(--success); }
.badge-info { background: var(--info-light); color: var(--info); }

/* Admin-adjustable home page section colors. Each defaults to the main
   accent color/tint (set below) unless an admin overrides it in Admin
   Settings > Website Customization, which writes --section-* directly onto
   the root element (see js/config.js applySectionColor). */
:root {
  --section-process: var(--accent);
  --section-process-light: var(--accent-light);
  --section-knowledge: var(--accent);
  --section-knowledge-light: var(--accent-light);
  --section-pricing: var(--accent);
  --section-pricing-light: var(--accent-light);
}
.badge-section-process { background: var(--section-process-light); color: var(--section-process); }
.badge-section-knowledge { background: var(--section-knowledge-light); color: var(--section-knowledge); }
.badge-section-pricing { background: var(--section-pricing-light); color: var(--section-pricing); }
#pricing .pricing-card.featured { border-color: var(--section-pricing); }

/* Feature Cards */
.feature-card { padding: 2rem; text-align: center; }
.feature-icon {
  width: 64px; height: 64px; border-radius: var(--radius-lg);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 1.5rem; font-size: 1.5rem;
}
.feature-icon.orange { background: var(--accent-light); color: var(--accent); }
.feature-icon.blue { background: var(--info-light); color: var(--info); }
.feature-icon.green { background: var(--success-light); color: var(--success); }
.feature-card h3 { margin-bottom: 0.75rem; font-size: 1.25rem; }

/* Feature rows - editorial list replacing the uniform 3-up icon grid */
.feature-rows { border-top: 1px solid var(--border); }
.feature-row {
  display: grid; grid-template-columns: 90px 1fr; gap: 1.75rem;
  padding: 2rem 0; border-bottom: 1px solid var(--border);
}
.feature-row-index { font-family: var(--font-mono); font-size: 0.85rem; color: var(--accent); padding-top: 0.35rem; }
.feature-row h3 { margin-bottom: 0.5rem; }
.feature-row p { max-width: 640px; }

/* "Why Skill Ready Techs" (index.html > section#features) - white copy.
   ------------------------------------------------------------------------
   This section previously inherited the light page background (--bg) with
   dark --text copy on top of it. White text alone would have been invisible
   there, so the section is given the deep navy primary surface as a
   backdrop and every piece of copy inside it is set to white. It now reads
   as a deliberate dark band between the two light sections either side of
   it, matching the hero and footer, which use the same navy.

   To revert to dark-on-light: delete this whole block. To keep the white
   text but drop the dark band, remove only the `background` and
   `border-top`/`border-bottom` declarations from #features below - but note
   the copy will then be unreadable on a light background. */
#features {
  background: linear-gradient(160deg, var(--primary) 0%, var(--primary-light) 100%);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
#features .section-header h2,
#features .feature-row h3 { color: #ffffff; }
#features .section-header p,
#features .feature-row p { color: rgba(255, 255, 255, 0.92); }
/* The eyebrow label and the 01-07 row numbers keep the accent hue, lightened
   so they stay legible against the navy rather than disappearing into it. */
#features .eyebrow { color: #93c5fd; }
#features .feature-row-index { color: #93c5fd; }
/* Row rules have to lighten too - the default --border is a light grey that
   is invisible on this ground. */
#features .feature-rows { border-top-color: rgba(255, 255, 255, 0.16); }
#features .feature-row { border-bottom-color: rgba(255, 255, 255, 0.16); }
/* The generic ".section + .section" separator that js/../styles.css draws
   between sections would show as a light seam on top of the navy. */
#features::before { display: none; }

/* How It Works - For Technicians / For Employers side-by-side comparison.
   Each side gets its own tinted card (accent blue vs. info blue, matching
   the numbered-step circles already used inside each column) with a top
   accent bar, so the two audiences read as clearly separate tracks rather
   than one continuous list that happens to have a heading in the middle. */
.how-it-works-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 2.5rem; align-items: start;
}
.how-it-works-col {
  position: relative; padding: 2rem 1.75rem 1.75rem; border-radius: var(--radius-lg);
  border: 1px solid var(--border); overflow: hidden;
}
.how-it-works-col::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 4px;
}
.how-it-works-col-tech { background: var(--section-process-light); }
.how-it-works-col-tech::before { background: var(--section-process); }
.how-it-works-col-employer { background: var(--info-light); }
.how-it-works-col-employer::before { background: var(--info); }

@media (max-width: 768px) {
  .how-it-works-grid { grid-template-columns: 1fr; gap: 2rem; }
  .how-it-works-col { padding: 1.75rem 1.5rem 1.5rem; }
}

/* Pricing */
.pricing-card { position: relative; padding: 2rem; text-align: center; transition: opacity 0.25s ease, transform 0.25s ease; }
.pricing-card.featured { border: 2px solid var(--accent); transform: scale(1.05); box-shadow: 0 12px 28px -12px rgba(2, 132, 199, 0.35); }
/* Employer-tier label - every pricing card except Technician gets this, so
   a mobile visitor scrolling through stacked, single-column cards (see the
   768px breakpoint below) can tell at a glance which tier belongs to which
   audience without having to scroll back up to the section intro copy. */
.pricing-employer-label {
  display: none; font-family: var(--font-mono); font-size: 0.95rem; font-weight: 800;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--info);
  background: var(--info-light); padding: 0.35rem 0.9rem; border-radius: 9999px; margin-bottom: 0.85rem;
}
@media (max-width: 768px) {
  .pricing-employer-label { display: inline-block; }
}
.pricing-price { font-size: 3rem; font-weight: 800; color: var(--text); margin: 1rem 0; }
.pricing-price span { font-size: 1rem; color: var(--text-muted); font-weight: 400; }
.pricing-features { list-style: none; margin: 2rem 0; text-align: left; }
.pricing-features li { padding: 0.5rem 0; display: flex; align-items: center; gap: 0.75rem; }
.pricing-features li i { color: var(--success); }

/* Admin Settings > Business & Growth can individually hide/show each
   employer plan tier (Starter / Professional / Enterprise) - see
   App.applyBranding() in js/app.js, which toggles each plan card's
   display between '' and 'none'. A fixed 4-column grid-template-columns
   would leave a dangling empty column whenever a tier is hidden, since
   CSS grid keeps the track count even when a track has nothing left in it.
   auto-fit re-flows around whatever is actually visible: it collapses any
   track with no item in it to zero width and lets the remaining cards
   grow to fill the row, so the pricing section re-spaces itself correctly
   no matter how many tiers are currently visible - 1, 2, 3, or all 4 -
   with no JS layout logic needed. Higher specificity (ID selector) than
   the general .grid-4 rule (including its breakpoint override below), so
   this always wins regardless of viewport width. */
#pricing-grid.pricing-grid {
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

/* Dashboard */
.dashboard { padding-top: 96px; min-height: 100vh; }
.dashboard-sidebar {
  width: 280px; background: var(--bg-white); border-right: 1px solid var(--border);
  height: calc(100vh - 72px); position: fixed; left: 0; top: 72px; overflow-y: auto;
  padding: 1.5rem; z-index: 100;
}
.dashboard-main { margin-left: 280px; padding: 2rem; min-height: calc(100vh - 72px); }
.sidebar-menu { list-style: none; }
.sidebar-menu li { margin-bottom: 0.25rem; }
.sidebar-menu a {
  display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem;
  border-radius: var(--radius); color: var(--text-light); font-weight: 500;
  position: relative; border-left: 3px solid transparent; transition: var(--transition);
}
.sidebar-menu a:hover, .sidebar-menu a.active { background: var(--accent-light); color: var(--accent-soft); }
.sidebar-menu a.active { border-left-color: var(--accent); font-weight: 600; }
.sidebar-menu a:hover { transform: translateX(2px); }
.sidebar-menu a.active:hover { transform: none; }
.sidebar-menu a i { width: 20px; text-align: center; }
.sidebar-section { margin-top: 2rem; padding-top: 2rem; border-top: 1px solid var(--border); }
.sidebar-section-title { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.075em; color: var(--text-muted); margin-bottom: 0.75rem; font-weight: 700; }

/* Stats Cards */
.stat-card { padding: 1.5rem; display: flex; align-items: center; gap: 1rem; }
.stat-icon { width: 48px; height: 48px; border-radius: var(--radius); display: flex; align-items: center; justify-content: center; font-size: 1.25rem; }
.stat-icon.blue { background: var(--info-light); color: var(--info); }
.stat-icon.green { background: var(--success-light); color: var(--success); }
.stat-icon.orange { background: var(--accent-light); color: var(--accent); }
.stat-icon.red { background: var(--danger-light); color: var(--danger); }
.stat-value { font-size: 1.5rem; font-weight: 700; color: var(--text); }
.stat-label { font-size: 0.875rem; color: var(--text-light); letter-spacing: 0.075em; text-transform: uppercase; font-weight: 700; }

/* Progress */
.progress-bar-bg { width: 100%; height: 8px; background: var(--border); border-radius: 9999px; overflow: hidden; }
.progress-bar-fill { height: 100%; background: var(--accent); border-radius: 9999px; transition: width 0.5s ease; }
.progress-bar-fill.success { background: var(--success); }
.progress-bar-fill.info { background: var(--info); }

/* Tables */
.table-container { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { padding: 1rem; text-align: left; border-bottom: 1px solid var(--border); }
.table th { font-weight: 600; color: var(--text-light); font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.05em; background: var(--bg); }
.table tr:hover { background: var(--bg); }
.table td { font-size: 0.95rem; }

/* Tags */
.tag { display: inline-flex; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 600; }
.tag-blue { background: #dbe8fb; color: #17509c; }
.tag-green { background: #d7f5e6; color: #056e4a; }
.tag-orange { background: var(--accent-light); color: var(--accent-soft); }
.tag-gray { background: #e8edf5; color: #35455c; }

/* Search Techs - distance search map (js/geosearch.js, Leaflet + OpenStreetMap) */
#tech-map { position: relative; }
#tech-map.hidden { display: none; }
.geo-origin-marker { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background: var(--hvac-orange); color: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.35); font-size: 0.9rem; }
.leaflet-popup-content { font-family: inherit; font-size: 0.85rem; }

/* Modals */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(15, 23, 42, 0.5); z-index: 2000;
  display: flex; align-items: center; justify-content: center; opacity: 0; visibility: hidden;
  transition: var(--transition);
}
.modal-overlay.active { opacity: 1; visibility: visible; }
.modal {
  background: var(--bg-white); border-radius: var(--radius-xl); max-width: 500px; width: 90%;
  max-height: 90vh; overflow-y: auto; box-shadow: var(--shadow-xl); transform: scale(0.95);
  transition: var(--transition); color: var(--text);
}
.modal-overlay.active .modal { transform: scale(1); }
.modal-header { padding: 1.5rem; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.modal-body { padding: 1.5rem; }
.modal-footer { padding: 1rem 1.5rem; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 0.75rem; flex-wrap: wrap; }
.modal-close { background: none; border: none; font-size: 1.25rem; cursor: pointer; color: var(--text-muted); flex-shrink: 0; }
.modal-close:hover { color: var(--text); }

/* A "compose" row (a message/reply textarea plus its send button side by
   side) - used by the New Message modal (admin.html) and the notification
   reply window (js/notifications.js). Stacks on narrow phone widths (see
   the 480px query below) instead of squeezing the textarea and button into
   a row too tight to use. */
.message-compose-row { display: flex; gap: 0.5rem; align-items: flex-end; }

/* Message-view surface: every place an actual message body is displayed for
   reading - the employer<->technician conversation windows, the admin<->user
   message thread (both on admin.html and on each dashboard), the notification
   reply detail modal, and the admin Message Inbox (contact-form submissions).
   Deliberately fixed to black text on a white card in a slim, low-contrast
   grey frame - independent of the accent color, dark/light section context,
   or which side sent it - so the message content itself always reads as
   clearly as possible instead of taking on a tinted "bubble" background that
   can wash out with certain accent colors. Sender is distinguished only by
   the alignment/label above the bubble, not by giving it a different fill. */
.msg-bubble {
  background: #ffffff;
  color: #14181f;
  border: 1px solid #d7dce2;
  border-radius: var(--radius);
  padding: 0.6rem 0.85rem;
  font-size: 0.875rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}
/* The admin Message Inbox card (contact-form submissions) and any other
   "view message" surface that isn't a chat bubble but still needs the same
   explicit black-on-white treatment. */
.msg-surface {
  background: #ffffff;
  color: #14181f;
  border: 1px solid #d7dce2;
  border-radius: var(--radius);
}
/* Wraps a whole scrollable message thread (a series of .msg-bubble elements)
   in the same low-contrast grey frame the per-conversation cards on the
   Employer/Technician "conversations" list already get, so every message
   thread on the site - the admin<->user thread on both dashboards, the
   admin's own Message User modal, and the notification reply detail modal -
   reads the same way: black-on-white bubbles sitting inside a grey frame,
   never floating directly on a white card/modal background. Applies equally
   on mobile - nothing here is width- or breakpoint-dependent. */
.msg-thread-frame {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.75rem;
}

/* File viewer (EPA 608 card + other credentials) - PDF branch. The "Open
   PDF" call-to-action always sits above the inline <embed> so it's visible
   without scrolling and works even on the mobile browsers that can't
   render the embed at all (see App.openFileViewer in js/app.js). */
.pdf-viewer-open-cta {
  display: flex; align-items: center; gap: 0.9rem; padding: 1rem;
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
  margin-bottom: 1rem;
}
.pdf-viewer-open-cta > i { font-size: 1.75rem; color: var(--danger); flex-shrink: 0; }
.pdf-viewer-open-cta > div { flex: 1; min-width: 0; }
.pdf-viewer-open-cta strong { display: block; word-break: break-word; }
.pdf-viewer-open-cta p { margin: 0.15rem 0 0; font-size: 0.8rem; color: var(--text-muted); }
.pdf-viewer-open-cta .btn { flex-shrink: 0; }

/* Uploaded-credential thumbnail (Admin > Verification Queue, Profile > Certifications) -
   a zoom-in cursor plus a magnifier overlay on hover/focus signals it opens
   a full-size viewer instead of just being a static image. */
.cred-thumb-btn { position: relative; transition: var(--transition); }
.cred-thumb-btn:hover, .cred-thumb-btn:focus-visible { border-color: var(--accent) !important; transform: scale(1.04); box-shadow: var(--shadow); }
.cred-thumb-btn::after {
  content: '\f00e'; font-family: 'Font Awesome 6 Free'; font-weight: 900; font-size: 0.85rem;
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  background: rgba(15, 27, 45, 0.45); color: white; opacity: 0; transition: var(--transition);
}
.cred-thumb-btn:hover::after, .cred-thumb-btn:focus-visible::after { opacity: 1; }

/* Toast */
.toast-container { position: fixed; bottom: 2rem; right: 2rem; z-index: 3000; display: flex; flex-direction: column; gap: 0.75rem; }
.toast {
  background: var(--bg-white); border-radius: var(--radius); padding: 1rem 1.5rem;
  box-shadow: var(--shadow-xl); border-left: 4px solid var(--accent); border: 1px solid var(--border); border-left-width: 4px;
  display: flex; align-items: center; gap: 0.75rem; min-width: 300px; color: var(--text);
  animation: slideIn 0.3s ease;
}
.toast.success { border-left-color: var(--success); }
.toast.error { border-left-color: var(--danger); }
.toast.info { border-left-color: var(--info); }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }
.toast.hiding { animation: slideOut 0.3s ease forwards; }

/* Exam */
/* Skill Exam page uses the exact same page background as every other page
   on the site (var(--bg)) - previously this had its own slightly different
   neutral (#fafafa vs the site's #F5F5F5), which made the background read
   as if it shifted when navigating to/from the exam. Every page now shares
   one literal background color with no per-page overrides. */

/* Same deep-navy gradient used for the hero, footer, and tech card, so the
   question prompt itself reads as the "instrument panel" - white text on
   the brand's dark ground - while the answer options below stay on their
   own white cards (.exam-option has its own --bg-white background) for a
   clear prompt/answer visual split. */
.exam-question { margin-bottom: 2rem; padding: 1.5rem; background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%); border-radius: var(--radius-lg); }
.exam-question-text { font-size: 1.125rem; font-weight: 600; margin-bottom: 1rem; color: #ffffff; }
.exam-options { display: flex; flex-direction: column; gap: 0.75rem; }
.exam-option {
  display: flex; align-items: center; gap: 1rem; padding: 1rem; background: var(--bg-white);
  border: 2px solid var(--border); border-radius: var(--radius); cursor: pointer; transition: var(--transition);
}
.exam-option:hover { border-color: var(--accent); }
.exam-option.selected { border-color: var(--accent); background: var(--accent-light); color: var(--text); }
.exam-option.correct { border-color: var(--success); background: var(--success-light); color: var(--text); }
.exam-option.incorrect { border-color: var(--danger); background: var(--danger-light); color: var(--text); }
.exam-option input { width: 20px; height: 20px; accent-color: var(--accent); }
.exam-timer {
  position: fixed; top: 88px; right: 2rem; background: var(--primary); color: white;
  padding: 0.75rem 1.5rem; border-radius: var(--radius); font-weight: 700;
  box-shadow: var(--shadow-lg); z-index: 50; display: flex; align-items: center; gap: 0.5rem;
}
.exam-timer.warning { background: var(--warning); color: var(--primary); }
.exam-timer.danger { background: var(--danger); animation: pulse 1s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }

/* Training Module */
.training-module { background: var(--bg-white); border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--border); }
.training-header { padding: 1.5rem; background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%); color: white; }
.training-content { padding: 2rem; }
.training-step { display: none; }
.training-step.active { display: block; animation: fadeIn 0.3s ease; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.training-nav { display: flex; justify-content: space-between; padding: 1rem 2rem; border-top: 1px solid var(--border); background: var(--bg); }
.training-progress { padding: 1rem 2rem; background: var(--bg); border-top: 1px solid var(--border); }
.diagram-container { background: var(--bg); border-radius: var(--radius); padding: 2rem; margin: 1rem 0; text-align: center; border: 2px dashed var(--border); }

/* Delta T (Cooling/Heating) graphics - HVAC Airflow Principles module.
   Two side-by-side cards at desktop width; below 600px they no longer have
   room to breathe next to each other (the 2rem labels wrap and the cards
   get squeezed), so they stack into a single column and step their type
   down so both graphics stay fully readable on mobile. */
.delta-t-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; }
.delta-t-card { text-align: center; padding: 1.5rem; border-radius: 0.5rem; color: #1e293b; }
.delta-t-card-cooling { background: var(--info-light); }
.delta-t-card-heating { background: var(--danger-light); }
.delta-t-label { font-size: 2rem; font-weight: 800; }
.delta-t-card-cooling .delta-t-label { color: var(--accent-soft); }
.delta-t-card-heating .delta-t-label { color: #991b1b; }
.delta-t-formula { font-size: 1.5rem; margin-top: 0.5rem; }
.delta-t-target { font-size: 1rem; margin-top: 0.5rem; color: #334155; }
@media (max-width: 600px) {
  .diagram-container { padding: 1.25rem 0.75rem; }
  .delta-t-grid { grid-template-columns: 1fr; gap: 1rem; }
  .delta-t-card { padding: 1rem; }
  .delta-t-label { font-size: 1.4rem; }
  .delta-t-formula { font-size: 1.1rem; }
  .delta-t-target { font-size: 0.9rem; }
}

/* Tech Certified Card */
.tech-card-id {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  border-radius: var(--radius-xl); padding: 0; position: relative; max-width: 640px; margin: 0 auto;
  box-shadow: var(--shadow-xl); overflow: hidden; color: white;
}
.tech-card-id-top { padding: 2rem 2rem 1.5rem; position: relative; }
.tech-card-id-top::after {
  content: ''; position: absolute; top: -60px; right: -60px; width: 200px; height: 200px;
  background: radial-gradient(circle, var(--accent) 0%, transparent 70%); opacity: 0.35;
}
.tech-card-brand { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem; position: relative; z-index: 1; }
.tech-card-brand-name { font-family: var(--font-display); font-weight: 700; font-size: 1rem; letter-spacing: 0.02em; }
.tech-card-verified-badge { background: var(--accent); color: white; font-size: 0.7rem; font-weight: 700; letter-spacing: 0.08em; padding: 0.3rem 0.75rem; border-radius: 9999px; text-transform: uppercase; }
.tech-card-id-name { font-size: 1.75rem; font-weight: 800; margin-bottom: 0.15rem; position: relative; z-index: 1; }
.tech-card-id-role { color: rgba(255,255,255,0.92); font-size: 0.95rem; position: relative; z-index: 1; }
.tech-card-id-meta { display: flex; gap: 1.5rem; margin-top: 1.25rem; position: relative; z-index: 1; flex-wrap: wrap; }
.tech-card-id-meta-item { font-size: 0.8rem; color: rgba(255,255,255,0.85); }
.tech-card-id-meta-item strong { display: block; color: white; font-size: 0.95rem; font-weight: 700; }
.tech-card-id-body { background: white; color: #1e293b; padding: 1.75rem 2rem 2rem; }
.tech-card-section-label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: #64748b; margin-bottom: 0.5rem; }
.tech-card-id-body .tag { margin: 0 0.3rem 0.3rem 0; }
.tech-card-contact-box { background: #f1f5f9; border: 1px solid #e2e8f0; border-radius: var(--radius); padding: 0.9rem 1rem; margin-top: 0.35rem; }
.tech-card-contact-row { display: flex; align-items: center; gap: 0.6rem; font-size: 1rem; font-weight: 600; color: #0f172a; margin-bottom: 0.5rem; }
.tech-card-contact-row:last-child { margin-bottom: 0; }
.tech-card-contact-row i { color: var(--accent); width: 18px; font-size: 1rem; flex-shrink: 0; }
/* The value takes the rest of the row and is allowed to shrink below its
   text width, so Exam.fitTechCardText() can scale a long email down to fit
   on one line; overflow-wrap is the backstop so it is never clipped. */
.tech-card-contact-value { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; line-height: 1.35; }
.tech-card-share-note { text-align: center; font-size: 0.8rem; color: var(--text-light); margin-top: 0.75rem; }
.tech-card-share-note i { color: var(--accent); margin-right: 0.25rem; }
.tech-card-id-id { font-size: 0.75rem; color: #64748b; border-top: 1px dashed #e2e8f0; margin-top: 1.25rem; padding-top: 1rem; display: flex; justify-content: space-between; }
.tech-card-avatar {
  width: 64px; height: 64px; border-radius: 50%; flex-shrink: 0; object-fit: cover;
  border: 2px solid rgba(255,255,255,0.5); background: rgba(255,255,255,0.15);
  display: flex; align-items: center; justify-content: center; color: white; font-weight: 700; font-size: 1.5rem;
}
.tech-card-id-headline { display: flex; align-items: center; gap: 1rem; position: relative; z-index: 1; }

/* Search & Filter */
.search-bar { display: flex; gap: 0.75rem; margin-bottom: 1.5rem; }
.search-input { flex: 1; }
.filter-tags { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1.5rem; }
.filter-tag { padding: 0.5rem 1rem; border-radius: 9999px; border: 1px solid var(--border); background: var(--bg-white); color: var(--text-light); cursor: pointer; font-size: 0.875rem; font-weight: 500; letter-spacing: 0.005em; transition: var(--transition); }
/* Hover and "selected" (.active) used to share the exact same solid-accent
   style, which made a filter chip you were merely hovering over (or had
   just tapped on a touchscreen, where the hover state can stick after the
   tap) look identical to one that was actually toggled on - so it was hard
   to tell, at a glance, which filters were really applied. Hover now gets
   its own lighter "about to select" tint, and only .active gets the solid
   fill, so a selected filter reads as clearly ON and a de-selected one
   reads as clearly OFF regardless of where the pointer happens to be.
   The hover tint itself is further scoped to devices that actually have a
   real hover state (a mouse/trackpad). Without this, tapping a chip on a
   touchscreen to DE-select it left it "stuck" showing the hover tint until
   the visitor tapped elsewhere, so a de-selected filter never actually
   looked like it did before it was ever touched - it looked like it was
   still half-selected. Gating hover behind (hover: hover) means a tap only
   ever toggles .active, so removing it always returns a chip to its exact
   original, untouched appearance. */
@media (hover: hover) and (pointer: fine) {
  .filter-tag:hover { background: var(--accent-light); color: var(--accent-soft); border-color: var(--accent); }
  .filter-tag.active:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
}
.filter-tag.active { background: var(--accent); color: white; border-color: var(--accent); }
.filter-tag:active { transform: scale(0.97); }
/* Candidate Profile modal (Search Techs > View Profile) - a navy/accent
   gradient banner echoes the Tech Certified Card treatment above, so the
   candidate profile feels like a natural continuation of the brand rather
   than a plain white popup. */
.candidate-modal-banner {
  display: flex; align-items: center; gap: 1.25rem; color: white;
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
  border-radius: var(--radius-lg); padding: 1.5rem; margin-bottom: 1.5rem;
  position: relative; overflow: hidden;
}
.candidate-modal-banner::after {
  content: ''; position: absolute; top: -50px; right: -50px; width: 180px; height: 180px;
  background: radial-gradient(circle, var(--accent) 0%, transparent 70%); opacity: 0.4;
}
.candidate-modal-avatar {
  width: 84px; height: 84px; border-radius: 50%; flex-shrink: 0; position: relative; z-index: 1;
  background: rgba(255, 255, 255, 0.15); border: 2px solid rgba(255, 255, 255, 0.5);
  display: flex; align-items: center; justify-content: center; color: white; font-size: 1.75rem; font-weight: 700;
}
.candidate-modal-name { color: white; margin-bottom: 0.15rem; font-size: 1.4rem; position: relative; z-index: 1; }
.candidate-modal-loc { color: rgba(255, 255, 255, 0.85); font-size: 0.9rem; margin-bottom: 0.65rem; position: relative; z-index: 1; }
.candidate-modal-badges { display: flex; gap: 0.5rem; flex-wrap: wrap; position: relative; z-index: 1; }
.candidate-modal-section-label { font-size: 0.7rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 0.6rem; display: flex; align-items: center; gap: 0.4rem; }

/* ------------------------------------------------------------------------
   HVAC Grid & Pro Card - technician profile card
   ------------------------------------------------------------------------
   Shared "profile card" treatment for anywhere a technician's profile is
   shown as a card: the listed-available-technicians results grid on
   Search Techs, and the Example Candidate Profile preview on the Home
   page hero. A left accent border in the brand HVAC blue is the visual
   anchor that ties both placements back to the same industry, and the
   .status-badge is the at-a-glance verification/availability signal. */
.hvac-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1.5rem;
  padding: 2rem 0;
}

.pro-card {
  display: flex;
  flex-direction: column;
  background: #ffffff;
  border: 1px solid var(--border);
  border-left: 5px solid var(--accent); /* Visual anchor for the industry */
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
  transition: var(--transition);
}
.pro-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }

.pro-card-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.pro-card-badges { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.4rem; }

.pro-card .status-badge {
  display: inline-block;
  background-color: #e0f2fe;
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
  text-transform: uppercase;
}
/* Status-badge variants for states other than the default "Verified" */
.pro-card .status-badge.featured { background: var(--hvac-orange-light); color: var(--hvac-orange-hover); }
.pro-card .status-badge.unavailable { background: #e8edf5; color: #566578; }
.pro-card .status-badge.available-now { background: var(--success-light, #dcfce7); color: var(--success, #16a34a); }

.pro-card h3 {
  margin: 0.75rem 0 0.25rem 0;
  font-size: 1.35rem;
}

.pro-card .specialties {
  font-size: 0.875rem;
  color: #64748b;
  margin-bottom: 1rem;
}

.pro-card .pro-card-salary { font-weight: 700; color: var(--primary); margin-bottom: 1rem; }
.pro-card .pro-card-salary span { font-weight: 400; color: var(--text-light); font-size: 0.8rem; text-transform: none; letter-spacing: normal; }
.pro-card .pro-card-about { font-size: 0.875rem; color: var(--text-light); flex: 1; margin-bottom: 1rem; }

.pro-card .tech-avatar { width: 56px; height: 56px; font-size: 1.1rem; }
.tech-avatar { border-radius: 50%; background: linear-gradient(135deg, var(--accent), var(--accent-hover)); display: flex; align-items: center; justify-content: center; color: white; font-size: 1.5rem; font-weight: 700; flex-shrink: 0; }
.tech-skills { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.75rem; }
.tech-actions, .pro-card-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: auto; padding-top: 1rem; }
.pro-card-actions .btn { flex: 1; white-space: nowrap; }

/* Certifications vs. skills - a calm, neutral pairing (steel-blue for the
   verified credential, plain slate for a self-listed skill) so the two
   read as clearly different things at a glance without resorting to a
   loud, mismatched color per tag. Both stay on the sans-serif body font
   already used by .tag site-wide. */
.tag-cert { background: rgba(2, 132, 199, 0.09); color: #2c6488; }
.tag-cert i { color: var(--accent); margin-right: 0.15rem; }
.tag-skill { background: #eef1f5; color: #47566b; }
.tag-skill i { color: #8493a8; margin-right: 0.15rem; }

/* Payment */
.payment-form { max-width: 500px; margin: 0 auto; }
.card-element { padding: 1rem; border: 2px solid var(--border); border-radius: var(--radius); background: var(--bg-white); margin-bottom: 1rem; }
.payment-methods { display: flex; gap: 1rem; margin-bottom: 1.5rem; }
.payment-method { flex: 1; padding: 1rem; border: 2px solid var(--border); border-radius: var(--radius); text-align: center; cursor: pointer; transition: var(--transition); }
.payment-method:hover { border-color: var(--accent); }
.payment-method.active { border-color: var(--accent); background: var(--accent-light); color: var(--text); }
.payment-method i { font-size: 1.5rem; margin-bottom: 0.5rem; display: block; }

/* Credential Verification (upload + validate) */
.cred-card { padding: 1.25rem; background: var(--bg); border-radius: var(--radius); border: 1px solid var(--border); }
.cred-card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem; gap: 1rem; }
.cred-status { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.75rem; font-weight: 700; letter-spacing: 0.02em; padding: 0.25rem 0.65rem; border-radius: 9999px; }
.cred-status.pending { background: #fdefd2; color: #8a5300; }
.cred-status.verified { background: #d7f5e6; color: #056e4a; }
.cred-status.failed { background: #fbdcdc; color: #a11b1b; }
.cred-status.none { background: #e6ebf3; color: #566578; }
.cred-status.on_file { background: #dbe8fb; color: #17509c; }
.cred-status.error, .cred-status.not_found { background: #fdeacd; color: #8a4b00; }
.cred-status.good { background: #d7f5e6; color: #056e4a; }
.cred-status.bad { background: #fbdcdc; color: #a11b1b; }
.cred-file-row { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.5rem; }
.cred-file-row input[type="file"] { flex: 1; font-size: 0.85rem; }
.cred-meta { font-size: 0.8rem; color: var(--text-light); margin-top: 0.5rem; }
.cred-list { display: flex; flex-direction: column; gap: 0.75rem; }

/* Skill picker (profile) */
.skill-picker {
  display: flex; flex-wrap: wrap; gap: 0.65rem;
  padding: 1rem; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
}
.skill-pill {
  padding: 0.55rem 1.1rem; border-radius: 9999px; border: 1.5px solid var(--border-strong);
  background: var(--bg-white); color: var(--text); font-size: 0.9rem; font-weight: 600;
  cursor: pointer; transition: var(--transition); letter-spacing: 0.01em;
}
.skill-pill:hover { border-color: var(--accent); color: var(--accent-soft); background: var(--accent-light); }
.skill-pill:focus-visible { outline: 2px solid var(--accent-soft); outline-offset: 2px; }
.skill-pill.selected {
  background: var(--accent); color: white; border-color: var(--accent); font-weight: 700;
  box-shadow: 0 0 0 1px var(--accent), 0 4px 10px -4px rgba(2, 132, 199, 0.4);
}
.skill-pill.selected::before { content: '\2713'; margin-right: 0.4rem; }
.skill-pill:active { transform: scale(0.97); }

/* Resource library */
.resource-toolbar { display: flex; gap: 0.75rem; margin-bottom: 2rem; flex-wrap: wrap; }
.resource-toolbar input { flex: 1; min-width: 240px; }
.video-link-card { display: block; padding: 0; overflow: hidden; text-decoration: none; }
.video-link-card img { width: 100%; display: block; aspect-ratio: 16/9; object-fit: cover; }
.video-link-card .video-link-body { padding: 1rem; }
.video-link-card .video-link-meta { font-size: 0.8rem; color: var(--text-light); margin-top: 0.25rem; }
.video-play-badge { position: relative; }
.video-play-badge::after {
  content: '\f04b'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center; color: white; font-size: 1.75rem;
  background: rgba(13,27,42,0.25); pointer-events: none;
}

/* Footer */
.footer { background: var(--primary); color: white; padding: 4rem 0 2rem; }
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 3rem; }
.footer-brand p { color: rgba(255,255,255,0.9); margin: 1rem 0; }
.footer-title { font-weight: 700; margin-bottom: 1rem; color: white; }
.footer-links { list-style: none; }
.footer-links li { margin-bottom: 0.5rem; }
.footer-links a { color: rgba(255,255,255,0.9); }
.footer-links a:hover { color: #60a5fa; }
.footer-bottom { border-top: 1px solid rgba(255,255,255,0.1); margin-top: 3rem; padding-top: 2rem; display: flex; justify-content: space-between; align-items: center; }
.footer-social { display: flex; gap: 1rem; }
.footer-social a { width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; color: white; }
.footer-social a:hover { background: var(--accent); }
/* X (Twitter) logo is inline SVG - Font Awesome 6.4.0 has no fa-x-twitter glyph. */
.footer-social .social-svg-icon { width: 16px; height: 16px; display: block; fill: currentColor; }

/* Animations */
@keyframes fadeInUp {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in { animation: fadeInUp 0.5s ease forwards; }
.delay-1 { animation-delay: 0.1s; }
.delay-2 { animation-delay: 0.2s; }
.delay-3 { animation-delay: 0.3s; }

/* --- Bottom tab bar --------------------------------------------------------
   Persistent mobile navigation for a signed-in technician - lets them jump
   straight between Dashboard, Training Modules, Skill Exam, and Profile from
   any of those four pages without opening the slide-out sidebar first. Desktop
   already has the always-visible sidebar for this, so the bar only renders
   under the 768px breakpoint (see the "Bottom tab bar" rule inside that
   media query below, which turns `display` on). */
.mobile-tab-bar {
  display: none;
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 900;
  background: var(--bg-white); border-top: 1px solid var(--border);
  box-shadow: 0 -4px 14px -6px rgba(15, 23, 42, 0.16);
  padding-bottom: env(safe-area-inset-bottom, 0);
}
.mobile-tab-bar a {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.25rem; padding: 0.6rem 0.25rem 0.65rem; color: var(--text-light);
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.01em;
}
.mobile-tab-bar a i { font-size: 1.05rem; }
.mobile-tab-bar a.active { color: var(--accent); }

/* Responsive */
@media (max-width: 1024px) {
  .dashboard-sidebar { transform: translateX(-100%); transition: transform 0.3s ease; }
  .dashboard-sidebar.open { transform: translateX(0); }
  .dashboard-main { margin-left: 0; }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 768px) {
  h1 { font-size: 2rem; }
  h2 { font-size: 1.75rem; }
  .mobile-menu-btn { display: block; }
  .hero { padding: 8rem 0 4rem; }
  .hero-inner { grid-template-columns: 1fr; }
  .hero-content { text-align: left; max-width: 100%; }
  .growth-strip { flex-direction: column; gap: 1.25rem; align-items: flex-start; }
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .section { padding: 3rem 0; }
  .pricing-card.featured { transform: none; }
  .hvac-grid { grid-template-columns: 1fr; }
  .feature-row { grid-template-columns: 1fr; gap: 0.5rem; }
  .footer-grid { grid-template-columns: 1fr; gap: 2rem; }
  .footer-bottom { flex-direction: column; gap: 1rem; text-align: center; }
  /* Previously this repositioned the timer to the bottom of the screen,
     where the fixed mobile bottom tab bar (.mobile-tab-bar, z-index 900)
     sits on top of it (this had z-index 50) - the timer was still running,
     just rendered underneath the tab bar and invisible to the candidate for
     the entire mobile exam. Keeping it pinned near the top-right, the same
     corner desktop uses, avoids that overlap entirely and shows it exactly
     like the non-mobile page does. */
  .exam-timer { top: 76px; right: 0.75rem; left: auto; bottom: auto; padding: 0.6rem 1rem; font-size: 0.9rem; }

  /* --- Header: spacing/sizing pass ---------------------------------------
     Below 768px the site name, nav links, and auth/user controls used to
     crowd into the same 72px-tall row with no size reduction at all, which
     is what made the header feel cramped and unbalanced on a phone. The
     wordmark, logo mark, and buttons all step down together here so the
     whole row keeps consistent, breathing-room spacing at phone widths. */
  .navbar-inner { height: 64px; }
  .logo { font-size: 1.05rem; gap: 0.5rem; }
  .logo-image, .logo-icon { width: 32px; height: 32px; font-size: 1rem; }
  .nav-actions { gap: 0.5rem; }
  .nav-actions .auth-buttons { display: flex; gap: 0.4rem; }
  .nav-actions .btn { padding: 0.55rem 0.9rem; font-size: 0.82rem; }
  /* At phone widths the avatar alone carries the dashboard button - the
     account name and the "Dashboard" tag both crowded the row, and the
     hamburger menu beside it spells out every destination in full. */
  .user-menu .user-name { display: none; }
  .user-home-tag { display: none; }
  .user-home-btn { padding: 0.25rem; }
  .mobile-menu-btn { padding: 0.3rem; line-height: 1; }

  /* The hamburger toggles `.nav-links.active`, but until now nothing ever
     styled that state, so tapping it did nothing visible - effectively a
     dead control sitting in the header. This gives it an actual mobile
     dropdown panel anchored under the fixed navbar. */
  .nav-links {
    display: none;
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--bg-white);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    padding: 0.5rem 0;
    max-height: calc(100vh - 64px);
    overflow-y: auto;
  }
  .nav-links.active { display: flex; }
  .nav-links li { width: 100%; }
  .nav-links a { display: block; padding: 0.9rem 1.5rem; font-size: 0.85rem; }
  .nav-links a::after { display: none; }

  /* Home page header only (see .navbar-home on index.html): the marketing
     nav links are all in-page anchors (or already in the footer), so rather
     than a hamburger that opened an empty-feeling menu, mobile visitors get
     the Log In / Sign Up buttons directly in that slot instead. */
  .navbar-home .mobile-menu-btn { display: none; }
  .navbar-home .nav-actions { flex: 1; justify-content: flex-end; }

  /* --- Dashboard shell: spacing pass -------------------------------------- */
  .dashboard { padding-top: 64px; }
  .dashboard-sidebar { top: 64px; height: calc(100vh - 64px); width: 260px; padding: 1.25rem; }
  .dashboard-main { padding: 1.25rem; }
  .card-header { padding: 1.1rem 1.25rem; }
  .card-body { padding: 1.25rem; }

  /* --- Search / filters: spacing pass ------------------------------------- */
  .search-bar { flex-direction: column; }
  .filter-tags { gap: 0.4rem; }
  .filter-tag { padding: 0.45rem 0.85rem; font-size: 0.8rem; }

  /* --- Modals -------------------------------------------------------------- */
  .modal { width: 92%; }
  .modal-header, .modal-body, .modal-footer { padding: 1.1rem; }
  /* Below this width we're almost always on a phone browser, where an
     embedded PDF data URL typically renders blank - so only the reliable
     "Open PDF" call-to-action shows; the desktop-only inline embed is
     dropped rather than left to fail silently. */
  .pdf-viewer-embed { display: none; }
  .pdf-viewer-open-cta { flex-direction: column; text-align: center; }
  .candidate-modal-banner { flex-wrap: wrap; text-align: center; justify-content: center; }
  .candidate-modal-loc, .candidate-modal-badges { justify-content: center; }

  /* --- Tech Certified Card --------------------------------------------- */
  .tech-card-id-top, .tech-card-id-body { padding-left: 1.25rem; padding-right: 1.25rem; }
  .tech-card-id-meta { gap: 1rem; }
  .tech-card-id-id { flex-direction: column; gap: 0.35rem; }

  /* --- Bottom tab bar (Technician pages - see .mobile-tab-bar) ----------- */
  .mobile-tab-bar { display: flex; }
  /* .has-tab-bar is set on <body> by the pages that render the bar (see
     .mobile-tab-bar markup) so its fixed height never covers page content. */
  body.has-tab-bar .dashboard-main { padding-bottom: 6rem; }
}

@media (max-width: 480px) {
  .container { padding: 0 1.1rem; }
  .logo { font-size: 0.88rem; gap: 0.4rem; white-space: nowrap; }
  .logo-image, .logo-icon { width: 28px; height: 28px; font-size: 0.85rem; }
  .nav-actions { gap: 0.35rem; }
  .nav-actions .auth-buttons { gap: 0.35rem; }
  .nav-actions .btn { padding: 0.5rem 0.65rem; font-size: 0.75rem; }
  .hero-buttons .btn { width: 100%; }
  .card-body { padding: 1.1rem; }
  .stat-card { padding: 1.1rem; gap: 0.85rem; }
  .stat-value { font-size: 1.25rem; }
  .pro-card-actions .btn, .tech-actions .btn { flex: 1 1 100%; }
  .location-fields-grid { grid-template-columns: 1fr; }

  /* --- New Message / reply modals: fit small phone screens -------------
     Below this width the modal's own 92% width (see the 768px query) still
     leaves a compose row (textarea + send button) or a modal footer's
     buttons too cramped to use - a wide textarea shrunk down next to a
     button, or two footer buttons overflowing the modal's edge rather than
     wrapping. Stacking the compose row, and letting footer buttons grow to
     fill a wrapped row, keeps every control fully usable with no
     horizontal overflow. */
  .modal { width: 94%; }
  .message-compose-row { flex-direction: column; align-items: stretch; }
  .message-compose-row .btn { width: 100%; }
  .modal-footer .btn { flex: 1 1 auto; }
}

/* Utility Classes */
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-left { text-align: left; }
.mt-1 { margin-top: 0.25rem; } .mt-2 { margin-top: 0.5rem; } .mt-4 { margin-top: 1rem; } .mt-6 { margin-top: 1.5rem; } .mt-8 { margin-top: 2rem; }
.mb-1 { margin-bottom: 0.25rem; } .mb-2 { margin-bottom: 0.5rem; } .mb-4 { margin-bottom: 1rem; } .mb-6 { margin-bottom: 1.5rem; } .mb-8 { margin-bottom: 2rem; }
.hidden { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border-width: 0; }
.w-full { width: 100%; }
.h-full { height: 100%; }
.min-h-screen { min-height: 100vh; }
.bg-white { background: var(--bg-white); }
.bg-primary { background: var(--primary); }
.text-white { color: white; }
.text-accent { color: var(--accent); }
.text-success { color: var(--success); }
.text-danger { color: var(--danger); }
.font-bold { font-weight: 700; }
.font-semibold { font-weight: 600; }
.text-sm { font-size: 0.875rem; }
.text-lg { font-size: 1.125rem; }
.text-xl { font-size: 1.25rem; }
.text-2xl { font-size: 1.5rem; }
.rounded { border-radius: var(--radius); }
.rounded-lg { border-radius: var(--radius-lg); }
.shadow { box-shadow: var(--shadow); }
.border { border: 1px solid var(--border); }
.overflow-hidden { overflow: hidden; }
.relative { position: relative; }

/* ============================================================================
   POLISH PASS
   ============================================================================
   Refinements within the site's identity - deep navy ground for hero/footer,
   blue accent, Georgia serif headlines, a bright working surface everywhere
   else. Everything here is either a craft detail the base sheet skipped or
   an accessibility floor it missed.
   ========================================================================== */

/* --- Focus visibility ---------------------------------------------------
   The base sheet styled :focus on inputs only, so keyboard users had no
   visible focus on buttons, links, filter chips, or sidebar items at all.
   :focus-visible keeps the ring off mouse clicks while restoring it for
   keyboard navigation. */
:where(a, button, [tabindex], .filter-tag, .skill-pill, .modal-close):focus-visible {
  outline: 2px solid var(--accent-soft);
  outline-offset: 2px;
  border-radius: var(--radius);
}
.form-input:focus-visible, .form-select:focus-visible, .form-textarea:focus-visible {
  outline: none;   /* the border + ring below already carry the state */
}

/* Skip-to-content affordance for keyboard users. */
.skip-link {
  position: absolute; left: 1rem; top: -3rem; z-index: 500;
  background: var(--accent); color: #fff; padding: 0.6rem 1rem;
  border-radius: var(--radius); font-weight: 600; font-size: 0.875rem;
  transition: top 0.18s ease-in-out;
}
.skip-link:focus { top: 1rem; }

/* --- Type scale --------------------------------------------------------
   Fixed 3.25rem headings overflowed small screens. Fluid sizing holds the
   same visual weight on desktop and stops the hero wrapping badly on
   phones. Georgia (serif) reads best at normal tracking - the tight
   negative letter-spacing below was tuned for the old sans display face
   and closes up a serif's counters, so headings keep natural spacing at
   every size. */
h1 { font-size: clamp(2.1rem, 1.35rem + 3.1vw, 3.25rem); letter-spacing: normal; }
h2 { font-size: clamp(1.6rem, 1.2rem + 1.7vw, 2.25rem); letter-spacing: normal; }
h3 { letter-spacing: normal; }

/* Eyebrow / section-label treatment: the uppercase micro-labels above
   headings read as noise without deliberate tracking. */
.section-header .badge,
.sidebar-section-title,
.tech-card-section-label,
.stat-label {
  letter-spacing: 0.075em;
  text-transform: uppercase;
  font-size: 0.7rem;
  font-weight: 700;
}
.section-header .badge { text-transform: none; letter-spacing: 0.02em; font-size: 0.8rem; }

/* Slightly more generous measure for body copy in prose contexts. */
.section-header p { line-height: 1.65; }

/* --- Surfaces ------------------------------------------------------------
   On this bright ground a hairline top highlight would just be an invisible
   white-on-white sheen, so cards get depth from a real (soft) shadow and a
   crisp border instead - the same trick used on paper-like UI generally. */
.card:hover { box-shadow: var(--shadow); border-color: var(--border); }
a .card:hover,
.card.is-interactive:hover,
.pricing-card:hover,
.video-link-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
}

/* Section rhythm: consecutive same-background sections ran together. A
   plain 1px rule reads as an unstyled template default, so the divider
   carries a small centered mark instead - a quiet nod to the gauge-cluster
   ticks used in the hero readout panel. */
.section { padding: clamp(3rem, 2rem + 4vw, 5rem) 0; position: relative; }
.section + .section { border-top: 1px solid var(--border); }
.section.bg-white + .section.bg-white { border-top: none; }
.section + .section::before {
  content: ''; position: absolute; top: -4px; left: 50%; transform: translateX(-50%) rotate(45deg);
  width: 7px; height: 7px; background: var(--bg-white); border: 1px solid var(--border);
}
.section.bg-white + .section.bg-white::before { display: none; }

/* --- Chips, tags, statuses -------------------------------------------- */
.cred-status { letter-spacing: 0.02em; font-weight: 700; }

/* --- Scrollbars -------------------------------------------------------
   Light, unobtrusive scrollbars to match the bright working surface. */
* { scrollbar-width: thin; scrollbar-color: #c2ccda transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: #c2ccda; border-radius: 9999px;
  border: 2px solid transparent; background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: #a9b6c8; background-clip: content-box; }

::selection { background: rgba(2, 132, 199, 0.22); color: #1e293b; }

/* --- Motion floor -----------------------------------------------------
   Respect a stated OS preference rather than animating regardless. */
@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;
  }
  .card:hover, a .card:hover, .pricing-card:hover, .pro-card:hover,
  .sidebar-menu a:hover, .btn-primary:hover { transform: none !important; }
}

/* ============================================================================
   SEAMLESS PAGE TRANSITIONS
   ============================================================================
   Every page in this multi-page site is its own document load, so there is
   no client-side router to animate between routes. Two complementary,
   zero-risk techniques cover that gap without touching any navigation logic:

   1. The native Cross-Document View Transitions API (Chrome/Edge/Opera as of
      2024+): opting in with `navigation: auto` makes the browser itself
      cross-fade the outgoing and incoming page automatically on same-origin
      link navigations - no JavaScript required, and it's simply ignored
      (no error, no visual difference) on browsers that don't support it yet.
   2. A CSS-only fade-in on the incoming page (`body` opacity animation),
      which works everywhere as the fallback so navigation never feels like a
      hard cut even without browser support for (1).

   Neither technique intercepts clicks, delays navigation, or touches the
   `window.location.href` redirects used throughout js/auth.js and js/app.js,
   so there is no risk of stalling or breaking any existing flow. */
@view-transition {
  navigation: auto;
}
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: pageFadeOut 0.22s ease both; }
  ::view-transition-new(root) { animation: pageFadeIn 0.28s ease both; }
}
@keyframes pageFadeOut { to { opacity: 0; } }
@keyframes pageFadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }

/* The plain `body` fade below is the fallback for browsers that DON'T
   support (1) above - Firefox and Safari as of this writing. Browsers that
   DO support cross-document view transitions already fade/slide the
   incoming page in via ::view-transition-new(root) - if this body
   animation also ran there, both effects would stack (the page fading in
   twice, on top of itself, in the same ~0.3s window), which looks like a
   stutter/flash rather than a single smooth transition. `view-transition-
   name` is a real, checkable CSS property whose support tracks the same
   browser versions that ship cross-document view transitions, so gating on
   it via @supports keeps this fallback scoped to exactly the browsers that
   actually need it. */
@supports not (view-transition-name: none) {
  body { animation: bodyFadeIn 0.32s ease; }
}
@keyframes bodyFadeIn { from { opacity: 0; } to { opacity: 1; } }

/* Back/forward navigation can restore a page straight from the browser's
   bfcache instead of re-running scripts - Chrome fires `pageshow` with
   `event.persisted: true` in that case. Without this, a page that was
   navigated away from mid-animation (e.g. the user clicked another link a
   split second after arriving) could be restored still holding a stale
   mid-fade opacity/transform from its computed style, instead of the fully
   visible state a restored page should always show immediately. */



/* Employer subscription chooser (payment.html) - lists whichever tiers an
   admin currently has enabled so the employer picks their plan at checkout
   instead of inheriting whichever one the link they clicked pointed at. */
.plan-choice {
  display: flex; align-items: center; gap: 0.9rem; width: 100%; text-align: left;
  padding: 1rem 1.1rem; background: var(--bg-white); cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius);
  transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.plan-choice:hover { border-color: var(--accent); }
.plan-choice.selected {
  border-color: var(--accent); background: var(--accent-light);
  box-shadow: 0 0 0 1px var(--accent);
}
.plan-choice-radio { color: var(--text-muted); font-size: 1.1rem; flex-shrink: 0; }
.plan-choice.selected .plan-choice-radio { color: var(--accent); }
.plan-choice-text { display: flex; flex-direction: column; gap: 0.2rem; flex: 1; min-width: 0; }
.plan-choice-text strong { font-size: 1rem; color: var(--text); }
.plan-choice-text span { font-size: 0.85rem; color: var(--text-light); }
.plan-choice-price {
  font-weight: 700; font-size: 1.05rem; color: var(--text);
  white-space: nowrap; flex-shrink: 0;
}
@media (max-width: 560px) {
  .plan-choice { flex-wrap: wrap; }
  .plan-choice-price { width: 100%; padding-left: 2rem; font-size: 0.95rem; }
}

/* Employer name at the head of a technician's message thread - a link
   through to that company's details (employer-profile.html). Styled to read
   as a link without losing the heading weight it had as plain text. */
.employer-thread-link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  margin-bottom: 0.5rem; color: var(--text); text-decoration: none;
  border-bottom: 1px solid transparent; transition: color 0.15s ease, border-color 0.15s ease;
}
.employer-thread-link:hover { color: var(--accent); border-bottom-color: var(--accent); }
.employer-thread-link i { font-size: 0.8rem; color: var(--text-muted); }
.employer-thread-link:hover i { color: var(--accent); }

/* Hired / No Longer Employed rows on the Employer Dashboard. Each row is a
   collapsed summary that expands to the full previous conversation with
   that technician - see renderHiringList() in employer-dashboard.html. */
.hire-row {
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden;
}
.hire-row-head {
  display: flex; align-items: center; gap: 0.75rem;
  padding: 0.85rem 1rem; flex-wrap: wrap;
}
.hire-row-toggle {
  display: flex; align-items: center; gap: 0.75rem; flex: 1; min-width: 180px;
  background: none; border: none; padding: 0; cursor: pointer;
  text-align: left; color: var(--text); font: inherit;
}
.hire-row-toggle:hover strong { color: var(--accent); }
.hire-row-toggle > i { color: var(--text-muted); font-size: 0.8rem; }
.hire-row-text { display: flex; flex-direction: column; gap: 0.15rem; flex: 1; min-width: 0; }
.hire-row-text strong { font-size: 0.95rem; }
.hire-row-text span { font-size: 0.78rem; color: var(--text-light); }
.hire-row-actions { display: flex; gap: 0.5rem; flex-shrink: 0; flex-wrap: wrap; }
.hire-row-thread {
  padding: 0 1rem 1rem; border-top: 1px dashed var(--border);
  margin-top: -0.25rem; padding-top: 0.9rem;
}
@media (max-width: 600px) {
  .hire-row-actions { width: 100%; }
  .hire-row-actions .btn { flex: 1; justify-content: center; }
}

/* Admin Settings > Website Data Storage - numbered Wix setup steps */
.srt-setup-steps { list-style: none; counter-reset: srt-step; margin: 0 0 1.25rem; padding: 0; display: flex; flex-direction: column; gap: 0.9rem; }
.srt-setup-steps > li {
  counter-increment: srt-step; position: relative;
  padding: 0.9rem 1rem 0.9rem 3.1rem;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.srt-setup-steps > li::before {
  content: counter(srt-step); position: absolute; left: 0.9rem; top: 0.85rem;
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: #fff; font-weight: 700; font-size: 0.85rem;
}
.srt-step-title { font-weight: 700; color: var(--text); margin-bottom: 0.55rem; line-height: 1.6rem; }
.srt-step-list { margin: 0; padding-left: 1.1rem; font-size: 0.85rem; color: var(--text-light); display: flex; flex-direction: column; gap: 0.3rem; }
.srt-step-list code, .srt-setup-steps .form-hint code { font-size: 0.8em; }
@media (max-width: 600px) {
  .srt-setup-steps > li { padding-left: 1rem; padding-top: 2.8rem; }
  .srt-setup-steps > li::before { top: 0.75rem; left: 1rem; }
}

/* ---- Version 30 ------------------------------------------------------- */

/* Live alerts: new message / hire request pop-ups (js/notifications.js) */
/* --- Notifications bell: "something new arrived" nudge --------------------
   Replaces the corner pop-up cards this file used to style (`.live-alert*`,
   removed in version 31 along with the code that created them). A new
   message now announces itself by counting up the bell's badge and running
   this once; it never covers the page, and it never opens anything. */
@keyframes notifBellNudge {
  0%, 100% { transform: rotate(0); }
  10%, 30%, 50% { transform: rotate(-12deg); }
  20%, 40%, 60% { transform: rotate(12deg); }
  70% { transform: rotate(0); }
}
.notif-bell.has-new i { display: inline-block; animation: notifBellNudge 1.1s ease-in-out 2; transform-origin: 50% 10%; }
.notif-bell.has-new .notif-badge { animation: notifBadgePop 0.35s ease-out; }
@keyframes notifBadgePop { from { transform: scale(0.4); } to { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .notif-bell.has-new i, .notif-bell.has-new .notif-badge { animation: none; }
}

/* Hire requests (js/hiring.js) */
.hire-request-row {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  padding: 0.75rem 1rem; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
}
.hire-request-row.pending { border-color: var(--accent); background: var(--accent-light); }
.candidate-modal-actions {
  display: flex; gap: 0.6rem; flex-wrap: wrap; align-items: center;
  margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--border);
}

/* Google Analytics consent banner (js/analytics.js) */
.consent-banner {
  position: fixed; left: 1rem; right: 1rem; bottom: 1rem; z-index: 2950; max-width: 720px; margin: 0 auto;
  display: flex; gap: 1rem; align-items: center; justify-content: space-between; flex-wrap: wrap;
  background: var(--bg-white); color: var(--text); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-xl); padding: 0.85rem 1rem; font-size: 0.875rem;
}
.consent-banner a { color: var(--accent); font-weight: 600; }
.consent-banner-actions { display: flex; gap: 0.5rem; }


/* --- Admin Settings: logo / favicon preview slots --------------------------
   Deliberately plain: no fill, no border-radius, no clipping, and the image
   inside is `contain`-fitted in a square box. The point of a preview is to
   show what the site will actually draw, so anything decorative here would
   make it a preview of something else. The `-dark` variant is the same slot
   on the navy the footer uses, so a logo with light or transparent areas can
   be checked against both surfaces before it is published. */
.logo-preview-slot {
  width: 56px; height: 56px; aspect-ratio: 1 / 1; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: 1px dashed var(--border); border-radius: 0;
}
.logo-preview-slot img {
  width: 100%; height: 100%; display: block;
  object-fit: contain; object-position: center;
}
.logo-preview-slot-dark { background: var(--primary); border-color: rgba(255,255,255,0.25); }


/* ===========================================================================
   LANDSCAPE / SHORT-VIEWPORT LAYOUT
   ===========================================================================
   Every breakpoint above this point is keyed on WIDTH alone, which is what
   broke the site when a phone was turned sideways. A phone in landscape is
   still a phone, but it reports a width of roughly 740-960px - above the
   768px "mobile" breakpoint and below the 1024px one. That band was never
   designed for, and the result on a sideways phone was:

     - the hamburger button was hidden (it only appears at <=768px), while
       the full inline desktop nav row was shown instead;
     - the dashboard sidebar was pushed off-canvas by the <=1024px rule, and
       with the hamburger gone there was no control left to bring it back -
       a technician had no navigation at all;
     - the bottom tab bar was hidden (it only appears at <=768px), removing
       the other way of getting around;
     - all the vertical spacing stayed at desktop values - a 72px header, 96px
       page top padding, 8rem hero padding, 4rem section padding - against a
       viewport only ~390px tall, so a screen's worth of scrolling bought
       almost no content;
     - the notch/camera housing, which sits on the LEFT or RIGHT edge in
       landscape, cut into the header, footer and page content, because no
       page opted into the safe-area insets.

   The rules below fix all of it by asking about height and orientation
   instead of width alone. They are last in the file so they win on
   specificity ties against the width-only rules above.

   `100dvh` (dynamic viewport height) is used in place of `100vh` wherever a
   full-height box is involved: on a phone `100vh` is the height with the
   browser chrome HIDDEN, so a `100vh` box in landscape is taller than the
   space actually available and its bottom sits permanently under the
   address bar. Every `dvh` rule is paired with a `vh` line immediately
   above it as the fallback for browsers that don't support it. */

/* --- Safe-area insets (all orientations) ---------------------------------
   Paired with `viewport-fit=cover` in each page's viewport meta tag. In
   portrait these evaluate to 0 on every device without a notch, so they are
   safe to apply unconditionally; in landscape they are what keeps the
   wordmark, the nav controls and the page content clear of the camera
   housing on one edge and the home indicator on the other. */
/* The home indicator sits along the bottom edge in BOTH orientations, so
   this one is unconditional. */
.mobile-tab-bar { padding-bottom: env(safe-area-inset-bottom, 0px); }

/* The camera housing only intrudes on the left/right edges when the device
   is rotated, so the horizontal insets are scoped to landscape. Scoping
   matters: applying them unconditionally would also overwrite the narrower
   page gutters the <=480px breakpoint sets in portrait, and the desktop
   .dashboard-main padding, for no benefit - in portrait these insets
   evaluate to 0 anyway. `max()` keeps whatever gutter the element already
   had and only grows it when the inset is genuinely larger. */
@media (orientation: landscape) {
  .container {
    padding-left: max(1.5rem, env(safe-area-inset-left, 0px));
    padding-right: max(1.5rem, env(safe-area-inset-right, 0px));
  }
  .navbar-inner {
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }
  .dashboard-main {
    padding-left: max(2rem, env(safe-area-inset-left, 0px));
    padding-right: max(2rem, env(safe-area-inset-right, 0px));
  }
  .dashboard-sidebar { padding-left: max(1.5rem, env(safe-area-inset-left, 0px)); }
  .mobile-tab-bar {
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }
  .consent-banner {
    left: max(1rem, env(safe-area-inset-left, 0px));
    right: max(1rem, env(safe-area-inset-right, 0px));
  }
}

/* Full-height boxes: prefer the dynamic viewport unit where it is supported. */
.dashboard { min-height: 100vh; min-height: 100dvh; }
.dashboard-main { min-height: calc(100vh - 72px); min-height: calc(100dvh - 72px); }
.dashboard-sidebar { height: calc(100vh - 72px); height: calc(100dvh - 72px); }
.min-h-screen { min-height: 100vh; min-height: 100dvh; }
.modal { max-height: 90vh; max-height: 88dvh; }

/* --- Phone in landscape ---------------------------------------------------
   The test is "short viewport, wide-ish screen, coarse pointer", which is a
   sideways phone and not a small laptop window: a laptop has a fine pointer
   and, at 1024px wide, far more than 500px of height. Matching on height
   rather than width is the whole point - it is the height that collapses
   when the phone is rotated, and the height that everything below reacts
   to. */
@media (orientation: landscape) and (max-height: 500px) {
  /* Bring back the two mobile navigation controls that the width-only rules
     were switching off at these widths. The hamburger panel and the bottom
     tab bar are the only navigation a sideways phone has, since the sidebar
     is off-canvas below 1024px. */
  .mobile-menu-btn { display: block; }
  .mobile-tab-bar { display: flex; }
  .navbar-home .mobile-menu-btn { display: none; }

  .nav-links {
    display: none;
    position: absolute;
    top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--bg-white);
    border-bottom: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    padding: 0.35rem 0;
    /* The panel must scroll rather than run off the bottom of a ~390px
       viewport once the 56px header is taken off it. */
    max-height: calc(100vh - 56px);
    max-height: calc(100dvh - 56px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav-links.active,
  body.nav-collapsed .nav-links.active { display: flex; }
  .nav-links li { width: 100%; }
  .nav-links a {
    display: block; padding: 0.6rem 1.5rem; font-size: 0.82rem;
    padding-left: max(1.5rem, env(safe-area-inset-left, 0px));
  }
  .nav-links a::after { display: none; }

  /* --- Vertical compaction ---------------------------------------------
     Roughly 390px of height has to carry a header, a footer and some actual
     content. Everything that was sized for a 900px-tall desktop window is
     stepped down here so a sideways phone gets a usable amount of content
     per screen instead of one heading. */
  .navbar-inner { height: 56px; }
  .logo { font-size: 1rem; gap: 0.5rem; }
  .logo-icon { width: 30px; height: 30px; font-size: 0.95rem; }
  .nav-actions .btn { padding: 0.45rem 0.8rem; font-size: 0.78rem; }
  .user-menu .user-name { display: none; }
  .user-home-tag { display: none; }

  .hero { padding: 6rem 0 2.5rem; }
  .hero-inner { gap: 1.5rem; }
  h1 { font-size: 1.9rem; }
  h2 { font-size: 1.5rem; }
  .section { padding: 2rem 0; }
  .footer { padding: 2.5rem 0 1.25rem; }
  .footer-bottom { margin-top: 1.5rem; padding-top: 1.25rem; }

  .dashboard { padding-top: 56px; }
  .dashboard-main {
    padding: 1rem;
    padding-left: max(1rem, env(safe-area-inset-left, 0px));
    padding-right: max(1rem, env(safe-area-inset-right, 0px));
    min-height: calc(100vh - 56px); min-height: calc(100dvh - 56px);
  }
  .dashboard-sidebar {
    top: 56px;
    height: calc(100vh - 56px); height: calc(100dvh - 56px);
    width: 260px; padding: 1rem;
  }
  .card-header { padding: 0.85rem 1.1rem; }
  .card-body { padding: 1.1rem; }

  /* The tab bar is fixed to the bottom of a very short screen, so pages that
     render it need the clearance or it covers their last control. */
  body.has-tab-bar .dashboard-main { padding-bottom: 5rem; }
  body.has-tab-bar .footer { padding-bottom: 5rem; }
  .mobile-tab-bar a { padding: 0.4rem 0.25rem 0.45rem; font-size: 0.62rem; }
  .mobile-tab-bar a i { font-size: 0.95rem; }

  /* Modals: a 90vh modal on a 390px-tall screen leaves no margin at all, and
     its own header/footer padding then eats most of what is left. */
  .modal { max-height: 92vh; max-height: 92dvh; width: min(92%, 640px); }
  .modal-header, .modal-footer { padding: 0.75rem 1.1rem; }
  .modal-body { padding: 1rem 1.1rem; }

  /* Corner alerts and the exam timer both sit in fixed positions measured
     from a header that just got shorter. */
  .exam-timer { top: 62px; right: 0.75rem; left: auto; bottom: auto; padding: 0.45rem 0.8rem; font-size: 0.82rem; }
  .notif-panel { max-height: calc(100vh - 80px); max-height: calc(100dvh - 80px); }

  /* login.html ships its own `.auth-page { min-height: 100vh; display: flex }`
     with a 45% navy brand panel. At this height the two columns side by side
     leave the form squeezed into half a short screen, so the brand panel is
     dropped and the form gets the full width - the same thing the page's own
     768px rule does in portrait. */
  .auth-page { min-height: 100vh; min-height: 100dvh; }
  .auth-sidebar { display: none !important; }
  .auth-form-container { width: 100% !important; padding: 1.5rem !important; }
}

/* A phone in landscape that is also narrow enough to still be reading the
   <=768px rules above (a small phone, or a split-screen window): those rules
   already handle the navigation, so this only trims the vertical spacing
   they leave at desktop values. */
@media (orientation: landscape) and (max-height: 430px) {
  .hero { padding: 5rem 0 2rem; }
  .section { padding: 1.75rem 0; }
  .navbar-inner { height: 52px; }
  .dashboard { padding-top: 52px; }
  .dashboard-sidebar { top: 52px; height: calc(100vh - 52px); height: calc(100dvh - 52px); }
  .exam-timer { top: 58px; }
}


/* --- Admin Settings > Email Delivery ---------------------------------------
   Setting up transactional email is the one job on this page with a real
   order to it - pick how you'll send, connect a service, prove you own the
   address - and getting the order wrong is what makes it confusing. So it is
   laid out as numbered steps rather than a flat wall of fields. */
.email-step { border-top: 1px solid var(--border); padding: 1.25rem 0; }
.email-step:first-of-type { border-top: 0; padding-top: 0; }
.email-step-last { padding-bottom: 0; }
.email-step-head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.6rem; font-size: 1rem; }
.email-step-num {
  width: 26px; height: 26px; flex-shrink: 0; border-radius: 50%;
  background: var(--accent); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.8rem; font-weight: 700;
}
.email-mode-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 0.85rem; }
.email-mode {
  display: flex; gap: 0.7rem; align-items: flex-start; cursor: pointer;
  padding: 0.9rem 1rem; border: 2px solid var(--border); border-radius: var(--radius);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.email-mode:hover { border-color: var(--accent); }
.email-mode.selected { border-color: var(--accent); background: var(--accent-light); }
.email-mode input { margin-top: 0.25rem; width: 16px; height: 16px; flex-shrink: 0; }
.email-mode strong { display: block; font-size: 0.9rem; margin-bottom: 0.3rem; }
.email-mode p { font-size: 0.8rem; color: var(--text-light); margin: 0; line-height: 1.55; }
.email-mode-tag {
  display: inline-block; margin-left: 0.35rem; padding: 0.1rem 0.45rem; border-radius: 9999px;
  background: var(--success-light, #dcfce7); color: var(--success);
  font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
}
.email-vercel-box {
  margin-top: 1rem; padding: 0.9rem 1.1rem; border-radius: var(--radius);
  background: var(--accent-light); border: 1px solid var(--accent);
}
.email-warn-box {
  padding: 0.85rem 1.1rem; border-radius: var(--radius);
  background: var(--warning-light, #fef3c7); border: 1px solid var(--warning);
}
.email-toggle-list { display: flex; flex-direction: column; gap: 0.6rem; }
.email-toggle-list label { display: flex; align-items: flex-start; gap: 0.6rem; font-size: 0.875rem; line-height: 1.5; }
.email-toggle-list input { width: 16px; height: 16px; margin-top: 0.15rem; flex-shrink: 0; }
/* The DNS records table. `overflow-x` on the wrapper (never on the page) is
   what keeps a long DKIM value from making the whole admin page scroll
   sideways on a phone. */
.dns-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--border); border-radius: var(--radius); }
.dns-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; min-width: 560px; }
.dns-table th, .dns-table td { padding: 0.55rem 0.7rem; text-align: left; border-bottom: 1px solid var(--border); vertical-align: top; }
.dns-table th { background: var(--bg); font-weight: 700; text-transform: uppercase; font-size: 0.68rem; letter-spacing: 0.04em; color: var(--text-light); }
.dns-table td code { font-size: 0.74rem; word-break: break-all; }
.dns-table tr:last-child td { border-bottom: 0; }
.dns-copy { background: none; border: 0; cursor: pointer; color: var(--accent); padding: 0.15rem 0.3rem; font-size: 0.8rem; }
.dns-status { display: inline-block; padding: 0.15rem 0.55rem; border-radius: 9999px; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.dns-status.verified { background: var(--success-light, #dcfce7); color: var(--success); }
.dns-status.pending { background: var(--warning-light, #fef3c7); color: var(--warning); }
.dns-status.failed { background: var(--danger-light, #fee2e2); color: var(--danger); }
