/* ============================================================================
   THE HOUSE DATE PICKER AND THE HOUSE SELECT — three pages, two voices.

   ⚠️ THIS FILE IS A SPLIT OUT OF send.css, FOR THE THIRD TIME AND THE SAME
   REASON. panel.css was split out of design.css when the event dock became
   the control panel; send.css was split out of design.css when the Send
   button appeared on /rentals and arrived with no styling at all. Now the
   calendar those rentals pages own is wanted on /contact, and send.css is a
   file /contact does not load. The rule send.css's own header writes down —
   "if a component is reachable from more than one page, its CSS goes in a
   file all of them load" — applies to the calendar the moment a third page
   asks for it.

   Namespaces: `dp*` (the date field, its calendar and the echo line, from
   rentals/datepick.js) and `hs*` (the house select, from houseselect.js).
   Both verified free across every other stylesheet.

   ⚠️ TWO VOICES, ONE COMPONENT, AND THE MODIFIER IS NOT OPTIONAL.
   Everything below paints for PAPER, which is what the send modal is. The
   /contact card is velvet, and the popups are `position: fixed` children of
   <body> — outside .form-card entirely — so they CANNOT inherit that card's
   colours through the cascade. The caller passes `klass: 'dp--velvet'` and
   the block at the foot of this file repaints them. A velvet field with a
   paper calendar hanging off it is what happens when that argument is
   dropped, and nothing errors.

   ⚠️ LOADED BY /contact, /rentals AND /rentals/design. On the rentals pages
   it sits immediately before send.css, which is where these rules used to be
   and so keeps the cascade order they were written in.
   ============================================================================ */

/* ---------- the date field and its calendar ----------
 * The shape follows the reference Darrell picked: month and year on the left,
 * arrows on the right, a seven-column grid, the chosen day in a filled
 * circle. The colour is ours — amber on paper rather than that green — and
 * the type is the page's, serif for the month and the site's small-caps for
 * the weekday rail. */
.dplabel {
  display: block; font-family: var(--sans); font-size: 11px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted);
}
/* ⚠️ THE BOX IS THE WRAPPER, NOT THE INPUT. One control that takes typing and
   opens a calendar needs a single border around both, so the border, the
   padding and the focus ring belong to .dpfield and the input inside it is
   stripped bare. Putting them on the input instead draws a second rectangle
   around two thirds of the field. */
.dpfield {
  display: flex; align-items: center; gap: .4rem;
  position: relative; margin-top: .25rem;
  background: var(--ground); border: 1px solid var(--rule-line);
  border-radius: var(--radius-sm); padding: .5rem .6rem;
  transition: border-color .15s;
}
.dpfield:hover { border-color: var(--amber); }
/* focus-within, so the ring is the same whether she tabbed into the text or
   onto the calendar button — they are one control to look at. */
.dpfield:focus-within { border-color: var(--amber-deep); }
.dpfield input {
  flex: 1 1 auto; min-width: 0;
  background: none; border: 0; padding: 0;
  font-family: var(--sans); font-size: 1rem; letter-spacing: .06em;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.dpfield input:focus-visible { outline: none; }
.dpfield input::placeholder { color: var(--rule-line); letter-spacing: .08em; }
.dpcal {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer; color: var(--muted);
}
.dpcal:hover, .dpcal[aria-expanded="true"] { color: var(--amber-deep); }
.dpicon { width: 17px; height: 17px; flex: none; }

/* ⚠️ FIXED AND PARENTED TO <body>. The modal is overflow-y:auto and would
   clip a calendar anchored to the field. left/top come from JS. */
.dp {
  position: fixed; z-index: 320;
  width: min(20.5rem, calc(100vw - 3rem));
  background: var(--paper); border: 1px solid var(--rule-line);
  border-radius: var(--radius); box-shadow: 0 18px 44px rgba(15, 15, 14, .22);
  padding: .9rem .9rem 1rem;
  animation: dpin .18s var(--ease-out);
}
@keyframes dpin { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .dp { animation: none; } }

.dp-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: .7rem; }
.dp-title { font-family: var(--serif); font-size: 1.15rem; color: var(--ink); }
.dp-nav { display: flex; gap: .2rem; }
.dp-nav button {
  width: 30px; height: 30px; display: grid; place-items: center; padding: 0;
  border: 0; background: none; border-radius: 50%; color: var(--muted); cursor: pointer;
  transition: background .15s, color .15s;
}
.dp-nav button:hover, .dp-nav button:focus-visible { background: var(--ground); color: var(--amber-deep); }
.dp-nav svg { width: 15px; height: 15px; }

.dp-dow, .dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); }
.dp-dow { margin-bottom: .25rem; }
.dp-dow span {
  text-align: center; font-family: var(--sans); font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted);
}
.dp-grid { gap: 2px; }
.dp-day, .dp-mon {
  aspect-ratio: 1; display: grid; place-items: center; padding: 0;
  border: 0; background: none; border-radius: 50%;
  font-family: var(--text); font-size: .95rem; font-variant-numeric: tabular-nums;
  color: var(--ink); cursor: pointer;
  transition: background .14s, color .14s;
}
.dp-day:hover:not([disabled]), .dp-mon:hover:not([disabled]) { background: color-mix(in srgb, var(--honey) 28.000000000000004%, transparent); }
.dp-day[disabled], .dp-mon[disabled] { color: var(--rule-line); cursor: default; }
/* Today is marked with a ring; the CHOSEN day is filled. Two different
   statements, and using the same weight for both is how a calendar ends up
   looking like it has two selections. */
.dp-day.today:not(.on) { box-shadow: inset 0 0 0 1px var(--amber); }
.dp-day.on, .dp-mon.on {
  background: var(--amber-deep); color: var(--paper); font-weight: 500;
}
.dp-day.on:hover, .dp-mon.on:hover { background: var(--amber-deep); }

.dp-monthmode { width: min(17rem, calc(100vw - 3rem)); }
.dp-months { display: grid; grid-template-columns: repeat(3, 1fr); gap: .3rem; }
.dp-mon { aspect-ratio: auto; padding: .55rem .2rem; border-radius: var(--radius-sm); }

/* ⚠️ ONE LINE THAT IS ALWAYS THERE. It reads back the date in words, or says
   what is wrong with it, or shows the format while the field is empty — and
   it holds its line in all three states. The version that hid itself when
   there was nothing to say made the modal grow and shrink a line on every
   keystroke, which moved the buttons under the cursor. */
.dpecho {
  display: block; min-height: 1.25em; margin-top: .3rem;
  font-family: var(--text); font-size: .88rem; color: var(--ink);
}
.dpecho.bad { color: var(--amber-deep); }

/* ---------- the wrapper the native input gets on /contact ----------
 * ⚠️ /contact KEEPS ITS <input type="date"> AND ONLY BORROWS THE POPUP, and
 * that is the whole reason this page was cheap to do. The send modal replaces
 * the input with a masked text field because it has to carry its own state
 * machine; /contact does not. The native input stays the value holder, so
 * FormData, reportValidity(), autofill, typing and the mm/dd/yyyy segments all
 * keep working untouched — the only thing taken away from the browser is the
 * calendar it draws, which is the only part Darrell was looking at.
 *
 * ⚠️ THE NATIVE INDICATOR HAS TO GO WHEN OURS ARRIVES, or the field wears two
 * calendar buttons and the left one opens the nineties dialog we just
 * replaced. `.dpfield--native` is added by JS at mount time and by nothing
 * else, so the no-JS and coarse-pointer paths never see this rule and keep
 * the native control whole. */
.dpfield--native input[type="date"]::-webkit-calendar-picker-indicator,
.dpfield--native input[type="month"]::-webkit-calendar-picker-indicator {
  display: none;
}
/* .field is a grid with its own gap; .dpfield's margin was written for the
   modal, where it sits under a bare <span> label. */
.dpfield--native { margin-top: 0; }
.dpfield--native[hidden] { display: none; }

/* ---------- the house select ----------
 * ⚠️ THE <select> IS STILL THE FIELD. It is hidden, never removed and never
 * disabled: FormData skips disabled controls and nothing else, so the form's
 * payload, pages.js's `line('type')` and the mailto fallback all keep reading
 * the same node they always read. What is below is a viewer for it.
 *
 * ⚠️ MOUNTED ON FINE POINTERS ONLY. A phone's native select is a full-screen
 * OS wheel with a thumb-sized hit area, and Darrell asked to keep it:
 * "the drop-down looks fine on mobile ... I'm completely fine with it". So on
 * a touch device none of this exists and the select is simply the select. */
.hsbtn {
  display: flex; align-items: center; gap: .5rem; width: 100%;
  background: var(--ground); border: 1px solid var(--rule-line);
  border-radius: var(--radius-sm); padding: .5rem .6rem;
  font-family: var(--sans); font-size: 1rem; letter-spacing: .06em;
  color: var(--ink); text-align: left; cursor: pointer;
  transition: border-color .15s;
}
.hsbtn:hover { border-color: var(--amber); }
.hsbtn:focus-visible { outline: none; border-color: var(--amber-deep); }
.hsval { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Nothing chosen yet is a placeholder, and it reads like one. */
.hsbtn.empty .hsval { color: var(--muted); }
.hsicon { width: 17px; height: 17px; flex: none; color: var(--muted); transition: transform .18s var(--ease-out); }
.hsbtn[aria-expanded="true"] .hsicon { transform: rotate(180deg); }

/* Same escape as .dp, for the same reason: fixed, parented to <body>, so no
   ancestor's overflow or transform can clip it. left/top/width come from JS. */
.hslist {
  position: fixed; z-index: 320;
  margin: 0; padding: .25rem 0; list-style: none;
  max-height: 17rem; overflow-y: auto;
  background: var(--paper); border: 1px solid var(--rule-line);
  border-radius: var(--radius); box-shadow: 0 18px 44px rgba(15, 15, 14, .22);
  animation: dpin .18s var(--ease-out);
}
@media (prefers-reduced-motion: reduce) { .hslist { animation: none; } }
.hsopt {
  padding: .5rem .8rem; cursor: pointer;
  font-family: var(--text); font-size: .95rem; color: var(--ink);
}
/* ⚠️ THE ACTIVE ROW IS NOT A :hover — the same note .qlist carries on
   /rentals. Arrow keys move a cursor the pointer knows nothing about and
   aria-activedescendant is what a screen reader follows, so styling only
   :hover leaves a keyboard user with an announced selection they cannot see. */
.hsopt.here { background: color-mix(in srgb, var(--honey) 28%, transparent); }
.hsopt[aria-selected="true"] { font-weight: 500; }
.hsopt[aria-selected="true"]::after {
  content: '\2713'; float: right; margin-left: .8rem; color: var(--amber-deep);
}
/* ⚠️ THE PLACEHOLDER ROW TAKES NEITHER THE TICK NOR THE WEIGHT. It is
   aria-selected while nothing has been chosen, because a listbox needs a
   selected option to start its cursor on — but drawing the chosen mark on
   "Select one…" tells her she picked it. Same reasoning as .hsbtn.empty on
   the closed control: the words are a prompt, so they read like one. */
.hsopt[data-empty] { color: var(--muted); font-weight: 400; }
.hsopt[data-empty]::after { content: none; }

/* ============================================================================
   THE VELVET VOICE — /contact only, and applied by class rather than cascade.

   ⚠️ THESE POPUPS ARE NOT INSIDE .form-card. They are fixed children of
   <body>, which is the whole point of parenting them there, and it means the
   card's velvet cannot reach them. `dp--velvet` and `hs--velvet` are put on
   the popup by the mounting call and are the only thing standing between the
   /contact form and a paper calendar hanging off a near-black field.

   Palette is the expanded rail's, the same one the card itself was ported to
   on 2026-09-03: velvet ground, gilt edge, emerald-lit type, champagne for
   the one thing being pointed at. --champagne on velvet is 12.78:1 and
   --emerald-lit is 5.86:1.

   ⚠️ THE CHOSEN DAY FILLS WITH GILT, NOT CHAMPAGNE, and the difference is a
   rule rather than a preference. emerald.css:589 counts champagne on a dark
   ground as a gilt event and the system permits one per page; /contact has
   already spent it on the card border and the Send button. Gilt fill with
   velvet type measures 6.7:1 and is the same pairing .cpanel__btn uses.
   ============================================================================ */
.dp--velvet, .hs--velvet {
  background: var(--velvet); border-color: var(--gilt);
  box-shadow: 0 18px 44px rgba(10, 9, 8, .55);
  color-scheme: dark;
}
.dp--velvet .dp-title { color: var(--emerald-lit); }
.dp--velvet .dp-dow span { color: var(--velvet-soft); }
.dp--velvet .dp-nav button { color: var(--velvet-soft); }
.dp--velvet .dp-nav button:hover, .dp--velvet .dp-nav button:focus-visible {
  background: color-mix(in srgb, var(--gilt) 22%, transparent); color: var(--champagne);
}
.dp--velvet .dp-day, .dp--velvet .dp-mon { color: var(--velvet-text); }
.dp--velvet .dp-day:hover:not([disabled]), .dp--velvet .dp-mon:hover:not([disabled]) {
  background: color-mix(in srgb, var(--gilt) 22%, transparent);
}
/* ⚠️ DISABLED IS THE PAST, AND IT STILL HAS TO BE READABLE. --emerald-mid on
   velvet is 3.1:1: below the 4.5:1 body-text bar on purpose, because that is
   what "you cannot pick this" looks like, and above the 3:1 floor SC 1.4.3
   sets for the text of a disabled control. */
.dp--velvet .dp-day[disabled], .dp--velvet .dp-mon[disabled] { color: var(--emerald-mid); }
.dp--velvet .dp-day.today:not(.on) { box-shadow: inset 0 0 0 1px var(--emerald-lit); }
.dp--velvet .dp-day.on, .dp--velvet .dp-mon.on,
.dp--velvet .dp-day.on:hover, .dp--velvet .dp-mon.on:hover {
  background: var(--gilt); color: var(--velvet); font-weight: 500;
}
.hs--velvet .hsopt { color: var(--velvet-text); }
.hs--velvet .hsopt.here { background: color-mix(in srgb, var(--gilt) 22%, transparent); }
.hs--velvet .hsopt[aria-selected="true"]::after { color: var(--champagne); }
.hs--velvet .hsopt[data-empty] { color: var(--emerald-mid); }
