/* Hearth & Home
   ---------------------------------------------------------------------------
   One stylesheet, no framework, no build step. The CSP forbids inline styles,
   so everything lives here.

   Photographs are the content; the interface should get out of their way. Warm
   neutral ground, almost no colour of its own, nothing that competes with what
   is in the pictures. */

/* ---------------------------------------------------------------- type */
/* Playfair Display, matching the wordmark in the logo: a high-contrast
   Didone with hairline thins. Self-hosted rather than linked from Google,
   because the CSP is default-src 'self' and would block it -- and because a
   private family gallery has no business telling Google who is browsing it.
   One variable file covers 400-700. SIL OFL 1.1; licence alongside it. */
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/playfair-display-v40.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Fallbacks are serifs that actually exist on the target platforms, so a
     blocked or slow font degrades to something in the same spirit rather than
     to Times at a display size. */
  --display: 'Playfair Display', 'Iowan Old Style', 'Palatino Linotype',
             Georgia, 'Times New Roman', serif;

  /* Sampled from the logo artwork: the brown of the wordmark, the burnt
     orange of the flame, the wheat of the leaves. --ember is a shade darker
     than the flame itself so link text clears 4.5:1 on the light ground;
     --ember-bright is the true logo orange, used where white sits on top. */
  --ink:          #2b211c;
  --ink-soft:     #6b5d54;
  --ground:       #faf7f4;
  --panel:        #ffffff;
  --line:         #e7ded5;
  --ember:        #b04a14;
  --ember-bright: #c05000;
  --ember-soft:   #f6eae1;
  --wheat:        #b08040;
  --focus:        #1d6fb8;

  --radius: 6px;
  --wrap:   1080px;

  --shadow: 0 1px 2px rgba(36, 31, 28, .06),
            0 4px 12px rgba(36, 31, 28, .05);
}

@media (prefers-color-scheme: dark) {
  :root {
    --ink:          #ece6e1;
    --ink-soft:     #a1978f;
    --ground:       #171412;
    --panel:        #211d1a;
    --line:         #362f2a;
    --ember:        #e2905f;
    --ember-bright: #e2905f;
    --ember-soft:   #2e211a;
    --wheat:        #c9a46a;
    --focus:        #6fb3e8;

    --shadow: 0 1px 2px rgba(0, 0, 0, .3),
              0 4px 12px rgba(0, 0, 0, .25);
  }
}

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

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 16px/1.6 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
}

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: 20px;
}

a { color: var(--ember); }
a:hover { text-decoration-thickness: 2px; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 3px;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: 8px;
  top: 8px;
  z-index: 10;
  padding: 8px 14px;
  background: var(--panel);
  box-shadow: var(--shadow);
}

/* --------------------------------------------------------------- furniture */

.site-head {
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}
.site-head .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 24px;
  align-items: baseline;
  justify-content: space-between;
  padding-block: 16px;
}

.brand { margin: 0; }
.brand a {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.01em;
}
.tagline {
  display: block;
  font-size: .8rem;
  color: var(--ink-soft);
  font-style: italic;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 20px;
  font-size: .9rem;
}
.site-nav a { color: var(--ink-soft); text-decoration: none; }
.site-nav a:hover { color: var(--ember); text-decoration: underline; }
.logout { margin: 0; }

.linkish {
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: var(--ink-soft);
  cursor: pointer;
  text-decoration: none;
}
.linkish:hover { color: var(--ember); text-decoration: underline; }

main { padding-block: 32px 64px; }

h1 {
  font-family: var(--display);
  font-size: 1.85rem;
  font-weight: 600;
  line-height: 1.2;
  margin: 0 0 .4em;
  letter-spacing: 0;
}
h2 { font-size: 1.05rem; margin: 0 0 .8em; font-weight: 600; }

.lede  { font-size: 1.05rem; color: var(--ink-soft); margin-top: 0; }
.quiet { color: var(--ink-soft); }
.small { font-size: .875rem; }

.site-foot {
  border-top: 1px solid var(--line);
  padding-block: 24px 40px;
  font-size: .85rem;
  color: var(--ink-soft);
}
.site-foot p { margin: .2em 0; }

/* ------------------------------------------------------------------ panels */

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 28px;
  box-shadow: var(--shadow);
}
.panel.narrow { max-width: 30rem; margin-inline: auto; }
.landing h1 { font-size: 2.2rem; }

.flash {
  margin: 0 0 16px;
  padding: 12px 16px;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--panel);
  font-size: .925rem;
}
.flash-error { border-color: var(--ember); background: var(--ember-soft); }
.flash-ok    { border-left: 3px solid var(--ember); }

/* ------------------------------------------------------------------- forms */

.stack { display: grid; gap: 6px; }
.stack label { font-size: .875rem; font-weight: 600; margin-top: 10px; }

input[type=email],
input[type=password],
input[type=text] {
  font: inherit;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--ground);
  color: var(--ink);
  width: 100%;
}
input:disabled { color: var(--ink-soft); }

.check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 400 !important;
  font-size: .9rem !important;
}
.check input { width: auto; }

button[type=submit], .button {
  display: inline-block;
  margin-top: 18px;
  padding: 11px 20px;
  font: inherit;
  font-weight: 600;
  color: #fff;
  background: var(--ember-bright);
  border: 1px solid transparent;
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  text-align: center;
}
button[type=submit]:hover, .button:hover { filter: brightness(1.08); }

.button-quiet {
  background: transparent;
  color: var(--ember);
  border-color: var(--line);
}
.actions { display: flex; flex-wrap: wrap; gap: 12px; }
.actions .button { margin-top: 8px; }

/* ------------------------------------------------------------------ albums */

.bucket { margin-bottom: 48px; }

.albums, .photos {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 22px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}

.album a { text-decoration: none; color: inherit; display: block; }

.album-cover {
  display: block;
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--line);
  box-shadow: var(--shadow);
}
.album-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .25s ease;
}
.album a:hover .album-cover img { transform: scale(1.03); }

.album-cover-empty {
  display: block;
  width: 100%;
  height: 100%;
  background:
    repeating-linear-gradient(45deg,
      transparent 0 10px,
      rgba(127, 127, 127, .07) 10px 20px);
}

.album-title {
  display: block;
  margin-top: 10px;
  font-weight: 600;
  line-height: 1.35;
}
.album-meta {
  margin: 2px 0 0;
  font-size: .825rem;
  color: var(--ink-soft);
}

.tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 99px;
  font-size: .72rem;
  font-weight: 600;
  text-transform: lowercase;
  background: var(--ember-soft);
  color: var(--ember);
}
.tag-quiet {
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink-soft);
}

/* ------------------------------------------------------------------ photos */

/* Photographs keep their own shape. Phone pictures are tall and holiday
   pictures are wide; cropping every one to a square throws away the part of
   the picture somebody chose to take.

   Columns rather than grid: the CSP forbids inline styles, so a per-image
   aspect-ratio cannot be emitted into the markup, and grid masonry is not
   supported widely enough to rely on. Columns give a true mixed-height layout
   with no JavaScript and no per-item styling at all. */
.photos {
  display: block;
  columns: 280px 4;
  column-gap: 18px;
}

.photo {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  margin: 0 0 18px;
}

.photo a {
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
}

.photo img {
  width: 100%;
  /* auto, explicitly: the markup carries real width/height attributes so the
     browser can reserve the box before the image lands, and without this the
     height attribute would stretch the picture instead. */
  height: auto;
  display: block;
  background: var(--line);
  transition: transform .3s ease;
}
.photo a:hover img,
.photo a:focus-visible img { transform: scale(1.02); }
.photo-meta {
  margin: 6px 0 0;
  font-size: .8rem;
  color: var(--ink-soft);
  line-height: 1.4;
}
.caption { display: block; color: var(--ink); }

/* -------------------------------------------------------------- member page */

.member-head, .album-head { margin-bottom: 32px; }
.crumbs { margin: 0 0 4px; font-size: .875rem; }
.crumbs a { color: var(--ink-soft); }

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .85em;
  background: var(--ember-soft);
  padding: 1px 5px;
  border-radius: 3px;
  word-break: break-all;
}

.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 28px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 10px;
}
.tabs a {
  padding: 5px 12px;
  border-radius: 99px;
  font-size: .875rem;
  text-decoration: none;
  color: var(--ink-soft);
  border: 1px solid var(--line);
}
.tabs a:hover { color: var(--ember); border-color: var(--ember); }
.tab-count { opacity: .6; margin-left: 3px; font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------ narrow */

@media (max-width: 560px) {
  .albums { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 16px; }
  /* On a phone, two columns of real photographs beats five of postage stamps. */
  .photos { columns: 140px 2; column-gap: 10px; }
  .photo  { margin-bottom: 10px; }
  .panel  { padding: 20px; }
  main    { padding-block: 24px 48px; }
  .site-head .wrap { flex-direction: column; align-items: flex-start; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* ------------------------------------------------- forms: the wider controls */

select, textarea {
  font: inherit;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--ground);
  color: var(--ink);
  width: 100%;
}
textarea { resize: vertical; min-height: 4.5em; }
input[type=file] { font: inherit; padding: 8px 0; color: var(--ink-soft); }

fieldset.kinds {
  margin: 18px 0 0;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
fieldset.kinds legend { padding: 0 6px; font-size: .875rem; font-weight: 600; }
fieldset.kinds .check { align-items: flex-start; margin-bottom: 10px; }
fieldset.kinds .check input { margin-top: 5px; }
fieldset.kinds .check span { font-size: .9rem; line-height: 1.5; }

/* An inline form sitting inside a table cell or a sentence. */
.inline-form {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  margin: 0;
}
.inline-form select { width: auto; padding: 5px 8px; font-size: .85rem; }
.inline-form button[type=submit] { margin-top: 0; }
.inline-form .linkish { font-size: .85rem; }

/* ------------------------------------------------------- settings furniture */

.two-up {
  display: grid;
  gap: 22px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  align-items: start;
}
.two-up h3 { margin: 0 0 12px; font-size: .95rem; }

table.members {
  width: 100%;
  border-collapse: collapse;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
table.members th,
table.members td {
  padding: 10px 14px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
table.members th {
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--ink-soft);
  font-weight: 600;
}
table.members tr:last-child td { border-bottom: 0; }

.inline-people {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 14px;
  padding: 0;
}
.inline-people li {
  padding: 3px 10px;
  border-radius: 99px;
  background: var(--ember-soft);
  color: var(--ember);
  font-size: .85rem;
}

/* The drift notice earns a little emphasis without shouting. */
.panel.drift { border-left: 3px solid var(--ember); margin-bottom: 28px; }
.panel.drift h2 { margin-bottom: .5em; }

.panel.danger { border-color: var(--ember); }
.panel.danger button[type=submit] { background: var(--ember); }

@media (max-width: 560px) {
  table.members th:nth-child(3),
  table.members td:nth-child(3) { display: none; }  /* "came from" is the least vital */
}

/* ------------------------------------------------------------------- brand */

.brand a {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
}
.brand-mark { display: block; flex: none; }
.brand-text { display: block; line-height: 1.15; }
.brand-name {
  display: block;
  font-family: var(--display);
  font-size: 1.3rem;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--ink);
  letter-spacing: -0.01em;
}

.landing-logo {
  display: block;
  width: 100%;
  max-width: 340px;
  height: auto;
  margin: 0 auto 8px;
}
.landing .lede { text-align: center; }

/* Available to screen readers, absent from the page. The landing page shows
   the wordmark as artwork, but the document still needs a real heading. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.copyable {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .8rem;
  width: 100%;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 28px 0 24px;
}
.panel h2 { margin-top: 0; }

/* --------------------------------------------------------- the ampersand */
/* Playfair's own "&" is an ornate two-loop swash; the logo uses a restrained
   angular one. The font carries no salt/ssNN/cvNN feature, so there is no way
   to ask for the other glyph -- it has to come from outside the font.

   This is the logo's actual ampersand, lifted out of the artwork as an alpha
   mask and painted with currentColor, so it follows the light and dark themes
   instead of being a fixed brown.

   The character itself stays in the markup and is merely pushed out of its own
   box, so screen readers still announce "and". And the whole thing is behind
   @supports: where masking is unavailable the plain "&" simply renders, rather
   than leaving a solid coloured rectangle. */
@supports ((-webkit-mask-image: url("")) or (mask-image: url(""))) {
  .amp {
    display: inline-block;
    width: .78em;
    height: .78em;
    vertical-align: -.06em;
    margin-inline: .04em;

    overflow: hidden;
    text-indent: 200%;
    white-space: nowrap;

    background-color: currentColor;
    -webkit-mask: url('/assets/ampersand-v1.png') no-repeat center / contain;
            mask: url('/assets/ampersand-v1.png') no-repeat center / contain;
  }
}

/* ------------------------------------------------------------ busy states */
/* Driven by hearth.js. Nothing here applies unless that file ran, so a visitor
   without JavaScript simply gets the site as it was. */

.busybar {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  height: 3px;
  z-index: 50;
  background: linear-gradient(90deg,
    transparent 0%, var(--ember-bright) 40%, var(--ember-bright) 60%, transparent 100%);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  opacity: 0;
  transition: opacity .15s ease;
  pointer-events: none;
}
.busybar.on {
  opacity: 1;
  animation: busy-sweep 1.1s ease-in-out infinite;
}

@keyframes busy-sweep {
  0%   { background-position: -40% 0; }
  100% { background-position: 140% 0; }
}

/* A submitting button keeps its width -- swapping the label for shorter text
   makes the whole row twitch, which reads as breakage rather than progress. */
button.is-busy {
  position: relative;
  color: transparent !important;
  cursor: progress;
  opacity: .9;
}
button.is-busy::after {
  content: '';
  position: absolute;
  inset: 0;
  margin: auto;
  width: 1em;
  height: 1em;
  border: 2px solid rgba(255, 255, 255, .45);
  border-top-color: #fff;
  border-radius: 50%;
  animation: busy-spin .7s linear infinite;
}
.button-quiet.is-busy::after,
.linkish.is-busy::after {
  border-color: rgba(127, 127, 127, .35);
  border-top-color: var(--ember);
}

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

/* A spinner that cannot be stopped is worse than none for anybody who gets
   motion sick, so the motion goes and a steady bar remains. */
@media (prefers-reduced-motion: reduce) {
  .busybar.on { animation: none; background-size: 100% 100%; opacity: .85; }
  button.is-busy::after { animation: none; border-top-color: rgba(255,255,255,.45); }
}

form[aria-busy="true"] { cursor: progress; }

/* Transient notices fade and collapse rather than simply vanishing, so the
   page does not jump under the reader's eye as one leaves. */
.flashes { transition: opacity .3s ease; }
.flashes.going {
  opacity: 0;
  /* Collapsing the space as well, so the content below settles smoothly
     instead of snapping upward when the element is removed. */
  max-height: 0;
  overflow: hidden;
  transition: opacity .3s ease, max-height .35s ease .05s;
}
.flashes .flash { cursor: pointer; }

@media (prefers-reduced-motion: reduce) {
  .flashes, .flashes.going { transition: none; }
}
