/*
 * Per-client branding lives here. To re-theme for a new client, edit only
 * this file (and swap the files in ../assets/ + config.php's logo_file/
 * logo_tint) -- nothing else in the app should need to change. The font
 * @import below is what makes that true for fonts too: swap the URL and
 * --font-heading/--font-body together and every page picks it up
 * automatically.
 *
 * Token groups (this is the surface a builder tool would expose):
 *   Background:  --gradient-start, --gradient-end   (page + card gradient)
 *   Accent:      --accent                            (buttons, links, table
 *                                                      header, focus rings)
 *   Text:        --text-color                         (headings, body, labels)
 *   Fonts:       --font-heading, --font-body
 *   Logo:        assets/<logo_file> + config['logo_tint'] (see partials/logo.php)
 *
 * --accent-hover, --text-muted and --card-border are *derived* from the
 * three colors above via color-mix() rather than picked separately, so a
 * re-theme only ever touches the handful of values in :root.
 */
/* Cormorant Garamond loads 700 and its italics because the entry-form heading is
   bold italic -- without the real cuts the browser synthesises them, which smears
   a serif badly. Keep both if you swap the heading font. */
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=Jost:wght@300;400;500;600;700&display=swap');

:root {
  --gradient-start: #F5F3EF;
  --gradient-end: #FFFFFF;
  --accent: #4B4E56;
  --accent-hover: color-mix(in srgb, var(--accent) 85%, black);
  --text-color: #101820;
  --text-muted: color-mix(in srgb, var(--text-color) 65%, transparent);
  --card-border: color-mix(in srgb, var(--accent) 35%, transparent);
  /* One red for every "this field still needs something" cue: the inline
     messages under the Register button, the red border those messages put on
     the field they name, and the phone field's international hint. */
  --field-error: #c0392b;
  --font-heading: 'Cormorant Garamond', Georgia, serif;
  --font-body: Jost, "Century Gothic", "Apple Gothic", sans-serif;
}

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  background: linear-gradient(135deg, var(--gradient-start) 0%, var(--gradient-end) 55%);
  background-attachment: fixed;
  font-family: var(--font-body);
  color: var(--text-color);
}
body { box-sizing: border-box; }

input, select, textarea { box-sizing: border-box; }

.portal-logo-container {
  text-align: center;
  margin: 50px 0 0;
}
/* Original-colors mode: a plain <img>, sized by its own intrinsic ratio. */
.portal-logo {
  max-width: 688px;
  width: 100%;
  height: auto;
  display: inline-block;
}
/* Solid-tint mode: the logo file is used purely as a mask shape, painted
   with --text-color, so any source art (any colors, any aspect ratio)
   becomes a flat on-brand mark without per-logo CSS math. */
.portal-logo-tinted {
  display: inline-block;
  width: 688px;
  max-width: 100%;
  height: 156px;
  background-color: var(--text-color);
  /* mask-image itself is set inline by partials/logo.php -- see the note there
     on why the URL can't come through a custom property. */
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
}

.logout-btn {
  position: fixed;
  top: 0;
  right: 20px;
  z-index: 1000;
  background: var(--accent);
  border: 1px solid var(--accent);
  color: #fff;
  border-radius: 0;
  padding: 8px 16px;
  font-size: 1.2rem;
  cursor: pointer;
  font-family: var(--font-body);
  box-shadow: 0 4px 12px rgba(16, 24, 32, 0.2);
}
.logout-btn:hover { opacity: 0.9; }

/* Passcode gate */
.gate-wrap {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 30px;
  max-width: 550px;
  margin: 50px auto;
  box-sizing: border-box;
  background: linear-gradient(to bottom right, var(--gradient-start), var(--gradient-end));
  border: 1px solid var(--card-border);
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(16, 24, 32, 0.1);
}
.gate-title {
  margin: 0 0 20px;
  font-family: var(--font-heading);
  font-size: 2rem;
  font-weight: 500;
  letter-spacing: 0.5px;
  color: var(--text-color);
  border-bottom: 1px solid var(--card-border);
  padding-bottom: 12px;
}
.gate-label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 1rem;
  color: var(--text-color);
}
.gate-label input {
  padding: 12px;
  border: 1px solid #ccc;
  border-radius: 0;
  font-size: 1rem;
  background: #fff;
  color: #111;
}
.gate-label input:focus { outline: 2px solid var(--accent); border-color: transparent; }
.gate-wrap button {
  padding: 12px;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  background: var(--accent);
  color: #fff;
  font-size: 1rem;
  font-weight: 600;
  transition: opacity 0.2s;
}
.gate-wrap button:hover { opacity: 0.9; }
.gate-msg {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Indented slightly from the input and button above it, so the line reads as a
     note about the field rather than another element in the stack. */
  padding-left: 20px;
  font-size: 0.85rem;
  color: var(--text-muted);
  margin: 0;
  font-weight: 500;
  min-height: 1.2em;
}
/* The icon only belongs on screen when there's a message beside it. */
.gate-msg .gate-msg-icon { display: none; width: 16px; height: 16px; flex: 0 0 auto; }
.gate-msg.has-msg .gate-msg-icon { display: block; }

/* Entry form card */
.portal-wrap { max-width: 680px; margin: 0 auto; padding: 0 20px; }
.entry-card {
  width: 80%;
  margin: 50px auto;
  padding: 50px 100px;
  box-sizing: border-box;
  background: linear-gradient(to bottom right, var(--gradient-start), var(--gradient-end));
  border: 1px solid var(--card-border);
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(16, 24, 32, 0.1);
}
.entry-form { display: flex; flex-direction: column; gap: 24px; }
/* "Gift Selection" / "Recipient Information": italic and half-strength, with a
   divider rule underneath. The 50% is applied to the text colour rather than as
   `opacity` on the element, so the divider rule keeps its full weight instead of
   fading with it. */
.form-subheader {
  margin: 0 0 20px;
  font-family: var(--font-heading);
  font-size: 2.5rem;
  font-weight: 400;
  font-style: italic;
  letter-spacing: 0.5px;
  color: color-mix(in srgb, var(--text-color) 50%, transparent);
  border-bottom: 1px solid var(--card-border);
  padding-bottom: 12px;
}
.entry-form label {
  display: flex;
  flex-direction: column;
  gap: 10px;
  font-size: 1.2rem;
  /* The body font's light cut -- nothing in this form carries weight. */
  font-weight: 300;
  color: var(--text-color);
  width: 100%;
  box-sizing: border-box;
}
/* The label's own text, held in a span so the required asterisk can sit beside it
   without joining the label's column layout. */
.entry-form .label-text { display: block; line-height: 1.2; }
/* Marks a field the form won't submit without. The phone's is toggled in JS --
   it's only required when the address is international. */
.entry-form .required-mark {
  margin-left: 4px;
  color: var(--field-error);
  font-weight: 400;
}
.entry-form .required-mark[hidden] { display: none; }
.entry-form input, .entry-form select, .entry-form textarea {
  padding: 14px;
  border: 1px solid #4C4E56;
  /* Square corners -- the button below keeps its radius, so the fields read as
     one flat block and the action stands apart from them. */
  border-radius: 0;
  font-size: 1rem;
  background: #ffffff;
  color: #111;
  box-sizing: border-box;
  font-family: inherit;
}
.entry-form input:focus, .entry-form select:focus, .entry-form textarea:focus {
  outline: 2px solid var(--accent);
}
.entry-form textarea { resize: vertical; }
/* Set by the form's own validation (see partials/entry-form.php) on any field
   whose "Please Add ..." message is currently showing, and cleared as soon as
   the field is filled in. */
.entry-form input.field-invalid,
.entry-form select.field-invalid,
.entry-form textarea.field-invalid { border-color: var(--field-error); }
.entry-form input.phone-hint::placeholder { color: var(--field-error); font-style: italic; }
/* The base gap is for the nested row inside "Arrive By Date" -- Month and Day are
   one value and stay tight. Only the form's own top-level rows get the wider gutter. */
.form-row { display: flex; gap: 14px; }
.entry-form > .form-row { gap: 24px; }
.form-row label { flex: 1; }
.form-row input.day-input { width: 80px; flex: 0 0 auto; }
/* Register and "View Delivery Schedule" are the same button in two places, so
   they're described once: a plain rectangle sized to its label, sitting at the left
   edge of the fields rather than stretching across them. The only difference between
   them is what's inside -- text, and the schedule link's icon. */
.entry-form > button,
.view-switch-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  align-self: flex-start;
  padding: 16px 40px;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  background: var(--accent);
  color: #fff;
  font-family: var(--font-body);
  font-size: 1.2rem;
  font-weight: 400;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.2s;
}
.entry-form > button { margin-top: 10px; }
.entry-form button:hover { opacity: 0.9; }
/* The submit button is a solid block with no border by design -- suppress the
   browser's own focus ring, which otherwise paints a blue outline on click. */
.entry-form button[type="submit"]:focus,
.entry-form button[type="submit"]:focus-visible { outline: none; }
.entry-form button:disabled { opacity: 0.5; cursor: default; }
.entry-msg {
  margin: 0;
  font-size: 1.2rem;
  min-height: 1em;
  line-height: 1.4;
  color: var(--text-color);
  text-align: center;
}
/* Errors read as a highlight rather than red text: the error colour moves to the
   background and the words go white. It sits on the inner span so the block hugs
   the sentence instead of stretching the full width of the centred paragraph. */
.entry-msg:empty { display: none; }
.entry-msg.err .entry-msg-text {
  display: inline-block;
  background: var(--field-error);
  color: #fff;
  padding: 6px 14px;
  border-radius: 8px;
}
.contact-footer {
  width: 90%;
  max-width: 680px;
  margin: 0 auto 30px;
  text-align: center;
  font-size: 0.9rem;
  color: var(--text-color);
}
.contact-footer a { color: var(--accent); }
/* Delivery table's copy of the footer lives in .sheet-footer-row, the line
   immediately under the table -- so it's vertically centered against "Updated
   h:mm:ss" for free via that row's align-items:center. margin-left:auto pushes
   it to the row's right end, which .sheet-footer-row's own padding-right:20px
   already keeps 20px shy of the table's right edge. */
.sheet-contact-footer {
  width: auto;
  max-width: none;
  margin: 0 0 0 auto;
  text-align: right;
  white-space: nowrap;
}

/* Gift Selection: 6 square tiles, 2 rows of 3, 20px gutters -- images are sized purely
   by the grid + aspect-ratio, not fixed pixel dimensions, so any source image "sizes
   down to match" automatically. Clicking a tile selects it (persists after the popup
   closes) and opens the popup; only the selected title is ever submitted. */
.gift-select-field { display: flex; flex-direction: column; gap: 10px; padding-bottom: 50px; }
.gift-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}
/* Scoped as ".gift-grid .gift-tile" (not just ".gift-tile") so this reset reliably
   beats ".entry-form button" -- the tile is a <button> inside .entry-form, and that
   rule's higher specificity would otherwise repaint it solid accent-color with 12px
   padding, hiding the image behind a colored box. */
.gift-grid .gift-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
}
.gift-grid .gift-tile-img-wrap { position: relative; display: block; width: 100%; }
.gift-grid .gift-tile-img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 0;
  border: 3px solid transparent;
  box-sizing: border-box;
  display: block;
  transition: border-color 0.15s, opacity 0.15s;
}
.gift-grid .gift-tile:hover .gift-tile-img { opacity: 0.85; }
.gift-grid .gift-tile.selected .gift-tile-img { border-color: var(--accent); }
/* Magnifying-glass hint, centered over the thumbnail on hover -- pointer-events:none
   so it never steals the tile's own click. */
.gift-grid .gift-tile-zoom {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #fff;
  filter: drop-shadow(0 1px 4px rgba(16, 24, 32, 0.6));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}
.gift-grid .gift-tile:hover .gift-tile-zoom { opacity: 1; }
.gift-grid .gift-tile-message {
  position: absolute;
  left: 3px;
  right: 3px;
  bottom: 3px;
  padding: 7px 12px;
  background: rgba(255, 255, 255, 0.5);
  border-radius: 0;
  font-family: var(--font-heading);
  font-size: 1.15rem;
  line-height: 1.25;
  color: var(--text-color);
  text-align: left;
  /* Clicks fall through to the tile, so the tag is never a dead spot on the way
     back into the popup to edit what it's showing. */
  pointer-events: none;
}
/* The clamp is on this inner span, not the tag itself: `overflow: hidden` clips at
   the padding edge, so a clamp on a padded box lets the third line show through the
   bottom padding. With the padding on the parent, this clips exactly at two lines.
   max-height is the backstop for browsers without -webkit-line-clamp -- they lose
   the ellipsis but still can't spill a third line. */
.gift-grid .gift-tile-message-text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  max-height: 2.5em; /* 2 lines at the 1.25 line-height above */
  overflow: hidden;
  text-align: center;
  font-style: italic;
  /* The message can be two lines -- keep the break, and let anything longer than
     the tile can hold trail off rather than pushing the tag up over the image. */
  white-space: pre-line;
  overflow-wrap: anywhere;
}
/* Curly quotes, set in the pseudo-elements rather than in the text itself so the
   stored message stays exactly what was typed. On a message long enough to clamp,
   the closing quote is cut with the rest -- the ellipsis is already saying the
   words run on. */
.gift-grid .gift-tile-message-text::before { content: '\201C'; }
.gift-grid .gift-tile-message-text::after { content: '\201D'; }
.gift-grid .gift-tile-message[hidden] { display: none; }

.gift-grid .gift-tile-title {
  font-size: 1.2rem;
  font-weight: 400;
  color: var(--text-color);
  text-align: center;
  line-height: 1.3;
  padding: 3px 10px;
  border-radius: 0;
  transition: background-color 0.15s, color 0.15s;
}
.gift-grid .gift-tile.selected .gift-tile-title,
.gift-grid .gift-tile:hover .gift-tile-title { background: var(--accent); color: #fff; }

.gift-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(16, 24, 32, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 3000;
}
.gift-modal-backdrop.show { opacity: 1; pointer-events: auto; }
.gift-modal {
  position: relative;
  background: #fff;
  border-radius: 14px;
  max-width: 520px;
  width: 100%;
  max-height: 85vh;
  box-shadow: 0 20px 60px rgba(16, 24, 32, 0.4);
}
/* The close button lives outside this scrolling area (a sibling, not a child) so it
   stays fixed in the modal's corner regardless of how far the content is scrolled. */
.gift-modal-scroll { max-height: 85vh; overflow-y: auto; border-radius: 14px; }
.gift-modal-img { width: 100%; aspect-ratio: 1; object-fit: cover; display: block; border-radius: 14px 14px 0 0; }
.gift-modal-body { padding: 26px 30px 30px; color: #111; }
.gift-modal-body h3 {
  margin: 0 0 12px;
  font-family: var(--font-heading);
  font-size: 1.6rem;
  font-weight: 500;
  color: var(--text-color);
}
.gift-modal-body p { margin: 0 0 16px; font-size: 0.95rem; line-height: 1.5; }
.gift-modal-includes-label { font-weight: 400; margin-bottom: 6px !important; }
.gift-modal-body ul { margin: 0; padding-left: 20px; font-size: 0.9rem; line-height: 1.6; }
/* Personalization -- only rendered for gifts flagged 'customizable' in
   config/gifts.php, so the popup is unchanged for everything else. Divided off
   from the contents list above it: it's an input, not more product copy. */
.gift-personalize {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--card-border);
}
.gift-personalize[hidden] { display: none; }
.gift-personalize-label {
  display: block;
  margin-bottom: 8px;
  font-size: 0.95rem;
  color: var(--text-color);
}
.gift-personalize-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 10px 12px;
  border: 1px solid #4C4E56;
  border-radius: 0; /* square, matching the entry form's fields */
  font-family: inherit;
  font-size: 1rem;
  line-height: 1.5;
  color: #111;
  background: #fff;
  /* Two lines, matching the two-line cap the field enforces -- no drag handle,
     since a third line can't be typed anyway. */
  resize: none;
}
.gift-personalize-input:focus { outline: 2px solid var(--accent); }
.gift-modal-select {
  display: block;
  width: 100%;
  margin-top: 22px;
  padding: 16px 22px;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  background: var(--accent);
  color: #fff;
  font-family: var(--font-body);
  font-size: 1.2rem;
  font-weight: 400;
  line-height: 1;
  transition: opacity 0.2s;
}
.gift-modal-select:hover { opacity: 0.9; }
.gift-modal-select:focus,
.gift-modal-select:focus-visible { outline: none; }

.gift-modal-close {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(16, 24, 32, 0.55);
  color: #fff;
  border: none;
  border-radius: 0;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  z-index: 1;
  transition: background 0.15s;
}
.gift-modal-close:hover { background: rgba(16, 24, 32, 0.8); }

/* Success toast: fixed, fades in/out, auto-dismisses after 3s (see entry-form.php).
   Errors stay in .entry-msg instead -- those need to stay visible until the user acts. */
.toast {
  position: fixed;
  top: 24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--accent);
  color: #fff;
  padding: 12px 22px;
  border-radius: 8px;
  font-size: 0.9rem;
  font-weight: 400;
  box-shadow: 0 8px 24px rgba(16, 24, 32, 0.25);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
  z-index: 2000;
}
.toast.show { opacity: 1; }

/* View Delivery Schedule moves to the bottom of the card, below the form,
   separated by a short centered divider. */
.view-switch-rule {
  width: 15%;
  /* Left-aligned, starting on the same edge as the two buttons it divides, and
     sitting the same distance from each of them. */
  margin: 20px 0;
  border: none;
  border-top: 1px solid var(--card-border);
}
/* Left-aligned so it lines up with Register above it. The rule between the two
   stays where it is -- it still reads as a divider, not as a centred flourish. */
.view-switch-row { text-align: left; }
.view-switch-link:hover { opacity: 0.9; }
/* Sized to the text rather than its own 20px, so the icon can't make this button
   a pixel taller than Register. */
.view-switch-link svg { width: 1em; height: 1em; flex: 0 0 auto; }

/* Delivery table */
.sheet-wrap { margin: 20px; padding: 20px; box-sizing: border-box; }
.sheet-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-wrap: wrap; padding-left: 20px; }
.sheet-toolbar input[type="search"] {
  width: 250px;
  padding: 9px 12px;
  border: 1px solid #ccc;
  border-radius: 8px;
  font-size: 0.8rem;
  background: #fff;
  color: #111;
}
.sheet-toolbar input[type="search"]:focus { outline: 2px solid var(--accent); border-color: transparent; }
.sheet-toolbar button {
  padding: 8px 14px;
  border: 1px solid transparent;
  background: var(--accent);
  color: #fff;
  border-radius: 0;
  cursor: pointer;
  font-size: 0.8rem;
  font-weight: 600;
  transition: opacity 0.2s;
}
.sheet-toolbar button:hover { opacity: 0.9; }
.sheet-toolbar button.refresh-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
.sheet-toolbar button.export-btn {
  padding: 6px 12px;
  font-size: 0.75rem;
}
.sheet-count { font-size: 0.8rem; color: var(--text-muted); margin-left: auto; margin-right: 20px; }
.sheet-scroll {
  overflow-x: auto;
  overflow-y: auto;
  max-height: 520px;
  border: 1px solid var(--accent);
  border-radius: 10px;
  background: #fff;
}
table { border-collapse: collapse; width: max-content; min-width: 100%; font-size: 0.85rem; }
th, td { text-align: center; padding: 12px 16px; border-bottom: 1px solid #ccc; white-space: nowrap; }
thead th {
  position: sticky; top: 0; z-index: 1; background: var(--accent); color: #fff; cursor: pointer;
  user-select: none; font-weight: 600; border-bottom: 2px solid var(--accent);
  line-height: 1; vertical-align: middle;
}
thead th:hover { background: var(--accent-hover); }
thead th.sort-active { background: var(--accent-hover); }
/* Header labels sit on one line when the column has room and wrap only when it
   doesn't; the fixed two-line box keeps the header row the same height either way. */
thead th .th-label {
  display: flex; align-items: center; justify-content: center;
  min-height: 2em; white-space: normal;
}
thead th .arrow { color: #fff; font-size: 0.75em; margin-left: 4px; }
tbody { background: #fff; color: #525A6C; }
tbody tr:nth-child(even) { background: #D9D9D6; }
/* Shipped rows in the Client view are read-only (see isRowLocked). Rather than
   fading the whole row with opacity -- which washed the text out to an
   unbranded grey and dimmed the stripe underneath it -- set the type to the
   brand grey and lift the background a little, so the row still reads as
   "settled, not editable" while staying legible. Employee rows are never
   locked, so this only ever affects the Client table. */
tbody tr.locked td {
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 5%, #fff);
}
td { font-weight: 400; line-height: 1.3; }

/* Delete column is frozen to the right edge so it stays reachable while the
   (often wide, horizontally-scrolled) Employee table scrolls. Needs an explicit
   opaque background per stripe -- a sticky cell with no background shows
   scrolled-past columns bleeding through underneath it. */
.sheet-scroll thead th:last-child {
  position: sticky;
  right: 0;
  top: 0;
  z-index: 2;
  box-shadow: -6px 0 6px -6px rgba(16, 24, 32, 0.25);
}
/* Not sortable (no click handler) and not draggable (see delivery-table.php) --
   so it shouldn't look interactive either: no hover/drag highlight, default cursor. */
.sheet-scroll thead th.col-actions,
.sheet-scroll thead th.col-actions:hover {
  background: var(--accent);
  cursor: default;
}
.sheet-wrap.dense thead th.col-actions { cursor: default; }
.sheet-scroll td.col-actions {
  position: sticky;
  right: 0;
  z-index: 1;
  background: #fff;
  box-shadow: -6px 0 6px -6px rgba(16, 24, 32, 0.25);
}
.sheet-scroll tbody tr:nth-child(even) td.col-actions { background: #D9D9D6; }

/* Export-selection column, pinned to the left edge and frozen the same way the
   delete column is pinned right, so the checkboxes stay reachable while the
   wide Employee table scrolls sideways. Same opaque-per-stripe requirement. */
.sheet-scroll thead th.col-select,
.sheet-scroll thead th.col-select:hover {
  position: sticky;
  left: 0;
  top: 0;
  z-index: 2;
  background: var(--accent);
  cursor: default;
  box-shadow: 6px 0 6px -6px rgba(16, 24, 32, 0.25);
}
.sheet-wrap.dense thead th.col-select { cursor: default; }
.sheet-scroll thead th.col-select { padding-left: 4px; padding-right: 4px; }
.sheet-scroll td.select-cell {
  position: sticky;
  left: 0;
  z-index: 1;
  background: #fff;
  box-shadow: 6px 0 6px -6px rgba(16, 24, 32, 0.25);
  width: 22px;
  padding-left: 4px;
  padding-right: 4px;
  text-align: center;
  /* The whole cell is the drag target, so make it feel like one and stop a
     sweep from turning into a text selection. */
  cursor: pointer;
  user-select: none;
}
.sheet-scroll tbody tr:nth-child(even) td.select-cell { background: #D9D9D6; }
.col-select input { cursor: pointer; vertical-align: middle; }
/* Row checkboxes are visually hidden -- the row tint already says what's selected,
   and the box was spending width to repeat it. Kept in the DOM (not removed) so
   click, drag-sweep, the header's indeterminate calculation and keyboard/screen
   reader access all keep working against the same element. */
.select-cell input.row-select {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  pointer-events: none;
}
.select-cell input.row-select:focus-visible {
  opacity: 1;
  width: auto;
  height: auto;
  pointer-events: auto;
}
/* Selected rows only recolor the selection-column cell -- the rest of the row
   keeps its normal even/odd stripe. */
.sheet-scroll tbody tr.row-selected td.select-cell {
  background: #4C4E56;
}
td[contenteditable="true"] { cursor: text; }
td[contenteditable="true"]:focus { outline: 2px solid var(--accent); background: #fff8ee; }
.tracking-link { position: relative; color: var(--accent); text-decoration: underline; font-weight: 700; }
.tracking-link:hover { opacity: 0.8; }
/* Employee's tracking-link cell is a single icon rather than the URL text -- the
   URL is never read in full, so it doesn't earn a column's width. Greyed and
   inert when there's no link to open, which is what tells the eye "not shipped
   yet" without needing a word for it. */
.sheet-scroll td.link-cell { width: 44px; }
.link-icon {
  position: relative;
  display: inline-block;
  line-height: 0;
  padding: 3px;
  border-radius: 4px;
  color: var(--accent);
  vertical-align: middle;
}
.link-icon-empty { color: #b6b8bc; }
.copy-cell:hover .link-icon { background: var(--accent); color: #fff; }

/* Floating editor for the tracking URL. Lives on <body>, so it can overhang the
   table's scroll container instead of being clipped by it. */
.url-popover {
  position: absolute;
  z-index: 1200;
  box-sizing: border-box;
  padding: 8px 10px;
  background: #fff;
  color: #111;
  border: 1px solid var(--accent);
  border-radius: 6px;
  box-shadow: 0 6px 20px rgba(16, 24, 32, 0.25);
  font-family: var(--font-body);
  font-size: 0.8rem;
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  overflow-wrap: anywhere;
  cursor: text;
}
.url-popover:focus { outline: none; }
td.col-recipient-name, td.col-address-block { font-weight: 700; }

/* Notes and Delivery Notes are the only free-text columns -- everything else is
   short enough that the global `white-space: nowrap` reads better, so these two
   opt out instead of flipping the default. The width is required, not cosmetic:
   `white-space: normal` alone lets an auto-layout table keep widening the column
   to fit the longest note. It must be `width`, not `max-width` -- an auto-layout
   table ignores max-width on cells and sizes to content regardless. Wrapped
   sentences are left-aligned; centred multi-line text is hard to scan. */
.sheet-scroll td.col-notes,
.sheet-scroll td.col-delivery-notes {
  white-space: normal;
  overflow-wrap: anywhere; /* a pasted URL or long token wraps instead of forcing a scroll */
  width: 120px;            /* deliberately narrow -- these wrap to several short
                              lines so the columns that must stay on one line
                              (address, tracking, email) get the horizontal room */
  text-align: left;
  line-height: 1.35;
}
.sheet-scroll thead th.col-notes,
.sheet-scroll thead th.col-delivery-notes { white-space: normal; }

/* Tracking numbers render as a pill (see PILL_FIELDS in delivery-table.php).
   A carrier code is one opaque token, so giving it a visible boundary makes it
   scannable in a dense row and shows the whole thing is a single copy target.
   Text stays selectable so it can also be highlighted by hand. */
.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 8%, #fff);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
  white-space: nowrap;
  cursor: pointer;
  user-select: text;
}
.pill:hover { background: color-mix(in srgb, var(--accent) 18%, #fff); border-color: var(--accent); }
/* The cell is only as wide as the chip needs; the chip carries the padding. */
.sheet-scroll td.pill-cell { padding-left: 6px; padding-right: 6px; }

/* Address column: a standard postage block (Company/Attn through Country -- Name
   is its own column). Hovering/clicking one element (a line, city, state, zip,
   country) highlights/copies just that element and double-click edits it; hovering
   the space around them (:not(:has(.addr-part:hover))) highlights/copies the whole
   thing. The block fills the entire cell (td padding moved onto it) so the whole
   column width is a click/hover target, not just the text itself. */
.sheet-wrap td.addr-cell,
.sheet-wrap.dense td.addr-cell { padding: 0; }
.addr-block {
  position: relative;
  display: block;
  width: 100%;
  box-sizing: border-box;
  text-align: left;
  white-space: normal;
  line-height: 1.6;
  padding: 6px 10px;
  cursor: pointer;
}
.addr-block:hover:not(:has(.addr-part:hover)) { background: color-mix(in srgb, var(--accent) 15%, transparent); }
.addr-block.addr-empty { cursor: default; color: #999; }
.addr-line { white-space: nowrap; }
.addr-part {
  position: relative;
  display: inline-block;
  cursor: pointer;
  border-radius: 3px;
  padding: 0 2px;
  margin: 0 -2px;
}
.addr-part:hover { background: var(--accent); color: #fff; }
.addr-part.editing {
  background: #fff;
  color: #111;
  outline: 2px solid var(--accent);
  cursor: text;
}
.addr-block.copied::after,
.addr-part.copied::after,
.gift-cell .copy-text.copied::after,
.gift-custom-tag.copied::after,
.copy-cell.copied > .copy-text::after,
.copy-cell.copied > .pill::after,
.copy-cell.copied > .tracking-link::after,
.copy-cell.copied > .link-icon::after,
/* Fallback for a copy cell whose value isn't in one of the wrappers above --
   the bubble is worth showing a little high rather than not at all. */
.copy-cell.copied:not(:has(> .copy-text, > .pill, > .tracking-link, > .link-icon))::after {
  content: "+ Copied";
  position: absolute;
  top: -24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--accent);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 600;
  padding: 3px 7px;
  border-radius: 4px;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 2px 6px rgba(16, 24, 32, 0.3);
  z-index: 10;
}

/* Same single-click-copy / double-click-edit treatment as the address parts, applied
   to whole cells: Name, Tracking Number, Tracking Link, Email, Phone, Delivery Notes,
   Notes. */
.copy-cell { position: relative; cursor: pointer; }
/* Same highlight as .addr-part:hover -- solid accent behind white text, hugging
   the value rather than washing the whole cell. Hover is detected on the cell so
   the full column width stays a copy target, but only the wrapper paints.
   box-decoration-break keeps the highlight clean where Notes wrap to 2+ lines. */
.copy-text, .pill { position: relative; border-radius: 3px; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
.copy-text { padding: 0 2px; margin: 0 -2px; }
.copy-cell:hover .copy-text,
.copy-cell:hover .pill { background: var(--accent); color: #fff; border-color: var(--accent); }
.copy-cell.editing {
  background: #fff;
  color: #111;
  outline: 2px solid var(--accent);
  cursor: text;
}

/* Gift Selection is the one two-part cell in the table -- the gift title, and a
   "Custom" tag underneath for rows carrying personalization text. Because the two
   are separate click targets, the hover highlight is per-element here instead of
   being driven by the cell the way single-value .copy-cell columns are (a cell-wide
   rule would light the title up while the pointer was on the tag). .gift-copy is
   set only for Employee, who is the only role that copies. */
.gift-cell { position: relative; white-space: normal; line-height: 1.45; }
.gift-cell .copy-text { position: relative; display: inline-block; font-weight: 700; }
.gift-cell.gift-copy .copy-text { cursor: pointer; }
.gift-cell.gift-copy .copy-text:hover { background: var(--accent); color: #fff; }
.gift-cell .copy-text.editing {
  background: #fff;
  color: #111;
  outline: 2px solid var(--accent);
  cursor: text;
}
/* Marks the row rather than showing the message -- that lives one click (copy) or
   double-click (popover) away. Sized with the gift name above it, and `fit-content`
   inside its own line so the hover highlight hugs the words instead of flooding
   the cell, the way every other copy target in the table behaves. */
.gift-custom-tag {
  position: relative;
  display: block;
  width: fit-content;
  margin: 2px auto 0;
  padding: 0 4px;
  border-radius: 3px;
  font-weight: 400;
  color: var(--text-muted);
  cursor: pointer;
}
.gift-custom-tag:hover { background: var(--accent); color: #fff; }

/* The multiline variant of the popover is a real <textarea> (see openUrlPopover)
   so a two-line message reads back with its line break intact. */
textarea.url-popover.multiline {
  display: block;
  resize: none;
  font-family: var(--font-body);
}

/* Employee's table is denser (smaller padding/font) since they see far more columns */
.sheet-wrap.dense th, .sheet-wrap.dense td { padding: 6px 10px; }
.sheet-wrap.dense table { font-size: 0.75rem; }

/* Employee can drag column headers to reorder them */
.sheet-wrap.dense thead th { cursor: grab; }
.sheet-wrap.dense thead th.dragging { opacity: 0.4; cursor: grabbing; }

/* toggleBar uses display:contents so its pills flow as direct items of this row,
   wrapping together with the status text right after them (20px gap: 8px row gap
   + 12px extra margin on the status text) rather than as a separate wrapped block. */
.sheet-footer-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 16px; padding-left: 20px; padding-right: 20px; }
.sheet-toggles { display: contents; }
.col-toggle {
  padding: 6px 12px; font-size: 0.75rem; border: 1px solid var(--accent);
  background: transparent; color: var(--accent); border-radius: 0; cursor: pointer; transition: background 0.2s;
}
.col-toggle:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.col-toggle.on { background: var(--accent); color: #fff; }

.sheet-status { font-size: 0.8rem; color: var(--text-muted); margin: 0 0 0 12px; }
.sheet-status.err { color: #c0392b; }

/* Employee-only per-row delete (Actions column) */
.delete-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid transparent;
  color: var(--text-muted);
  cursor: pointer;
  padding: 5px 7px;
  border-radius: 0;
  transition: color 0.15s, background 0.15s;
}
.delete-btn:hover { color: #c0392b; background: rgba(192, 57, 43, 0.12); }

/* Google Places Autocomplete dropdown overrides */
.pac-container { background: #fff; border: 1px solid #ccc; border-top: none; font-family: inherit; }
.pac-item { border-top: 1px solid #eee; color: #333; padding: 8px 10px; }
.pac-item:hover { background: #f5f5f5; }
.pac-item-query { color: #111; }
.pac-matched { color: var(--accent); }

@media (max-width: 640px) {
  .entry-card { padding: 30px 24px; margin: 20px auto; }
  table { font-size: 0.75rem; }
  th, td { padding: 8px 10px; }
  .sheet-toolbar input[type="search"] { width: 100%; }
}
