/* ============================================================
   lexily
   palette : a moonlit garden. green is foliage under moonlight,
             gold is the light itself, violet is the sky behind.
   type    : fraunces (display) / quicksand (body) / caveat (hand)
   motif   : the moon phases. they run across the card as the
             divider, and tonight's phase is the one that's lit.
   sibling : same bones as iloily.com — one card, drifting motes,
             tabs, dt/dd rows, a shelf. dark instead of cream,
             because moons want a night sky to sit in.
   ============================================================ */

:root{
  --ink:   #150f22;   /* deepest sky            */
  --dusk:  #1e1436;
  --sky:   #2b1d4a;   /* the card               */
  --iris:  #6b4aa8;   /* mid violet, borders    */
  --lilac: #b9a3e3;   /* soft secondary text    */
  --fern:  #4a9d7a;   /* moonlit green          */
  --mint:  #7fd3ae;   /* green, lifted          */
  --moon:  #f0d78c;   /* gold                   */
  --brass: #c9a54e;
  --milk:  #f4efe2;   /* warm cream text        */

  --f-display: 'Fraunces', Georgia, serif;
  --f-body:    'Quicksand', ui-rounded, system-ui, sans-serif;
  --f-hand:    'Caveat', cursive;

  --r: 26px;
}

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

body{
  margin:0;
  min-height:100vh;
  background:
    radial-gradient(820px 560px at 88% -6%,  rgb(107 74 168 / .55) 0%, transparent 60%),
    radial-gradient(700px 520px at 6% 104%,  rgb(74 157 122 / .30) 0%, transparent 58%),
    radial-gradient(420px 320px at 78% 92%,  rgb(240 215 140 / .12) 0%, transparent 60%),
    linear-gradient(170deg, var(--dusk) 0%, var(--ink) 58%, #100b1a 100%);
  background-attachment:fixed;
  color:var(--milk);
  font-family:var(--f-body);
  font-size:16px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  display:grid;
  place-items:center;
  padding:clamp(46px,8vw,86px) clamp(14px,4vw,32px) clamp(40px,7vw,72px);
  overflow-x:hidden;
  animation:drift 30s ease-in-out infinite alternate;
}
@keyframes drift{
  0%   { background-position:0% 0%,    100% 100%, 50% 50%,  0% 0%; }
  100% { background-position:12% 10%,  88% 90%,   44% 58%,  0% 20%; }
}

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

.skip{
  position:absolute; left:-9999px; top:0; z-index:99;
  background:var(--moon); color:var(--ink);
  padding:12px 18px; border-radius:0 0 14px 0;
  font-family:var(--f-body); font-weight:700; font-size:.85rem;
}
.skip:focus{ left:0; }

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

/* ───────────────────── drifting sky ───────────────────── */

.sky{
  position:fixed; inset:0;
  overflow:hidden; pointer-events:none; z-index:0;
}

/* three real objects hanging in the sky, placed around the card rather than
   on it — the corners are the only space on the page nothing else wants.
   the moon is largest and faintest, so it reads as distance. */
.sky__moon,
.sky__face,
.sky__star{ position:absolute; height:auto; }

/* violet ground drains the green out of a 22%-opacity photograph, so the
   saturation is pushed hard to put it back */
.sky__moon{
  top:-8%; left:-10%;
  width:min(44vmin,400px);
  opacity:.24;
  filter:saturate(2.6) brightness(1.06) blur(.4px);
}
.sky__face{
  top:5%; right:3%;
  width:min(19vmin,178px);
  opacity:.55;
  transform:rotate(14deg);
  filter:brightness(1.14) saturate(1.15);
}
/* bottom left, diagonally opposite the moon and well clear of the wax seal
   on the card's other corner */
.sky__star{
  bottom:8%; left:4%;
  width:min(15vmin,140px);
  opacity:.3;
  transform:rotate(-12deg);
  filter:blur(.3px);
}

/* four-point star, cut with a clip-path so it costs nothing */
.mote{
  position:absolute;
  left:var(--x); bottom:-8%;
  width:var(--s); height:var(--s);
  background:var(--milk);
  opacity:0;
  clip-path:polygon(50% 0%, 59% 41%, 100% 50%, 59% 59%, 50% 100%, 41% 59%, 0% 50%, 41% 41%);
  animation:rise var(--d) linear infinite;
  animation-delay:var(--t);
}
/* a crescent: one gold disc with a second disc bitten out of it */
.mote--moon{
  clip-path:none;
  background:var(--moon);
  border-radius:50%;
  -webkit-mask:radial-gradient(circle at 128% 50%, transparent 58%, #000 59%);
          mask:radial-gradient(circle at 128% 50%, transparent 58%, #000 59%);
}

@keyframes rise{
  0%   { transform:translate(0,0) scale(.5) rotate(0deg);              opacity:0; }
  14%  {                                                              opacity:.8; }
  86%  {                                                              opacity:.55; }
  100% { transform:translate(var(--w),-112vh) scale(1.05) rotate(140deg); opacity:0; }
}

.stage{ position:relative; z-index:1; width:100%; max-width:740px; }

/* revealed only on phones, where the fixed sky is useless. z-index:-1 puts it
   under the card's background — .stage is the stacking context, so it cannot
   fall through the page — and only the arc past the corner shows. */
.stage__moon{ display:none; }

/* ───────────────────────── the card ───────────────────────── */

.card{
  position:relative;
  background:
    radial-gradient(520px 300px at 50% -12%, rgb(107 74 168 / .38) 0%, transparent 70%),
    var(--sky);
  border-radius:var(--r);
  padding:clamp(20px,3.4vw,30px);
  box-shadow:
    0 0 0 1px rgb(240 215 140 / .18),
    0 1px 0 rgb(255 255 255 / .06) inset,
    0 26px 60px -20px rgb(0 0 0 / .7);
}

/* ─────────────────────── top: name + player ─────────────────────── */

.top{ padding-bottom:clamp(14px,2.4vw,18px); }

.mark{
  margin:0;
  font-family:var(--f-display);
  font-weight:600;
  font-size:clamp(1.32rem,3.6vw,1.6rem);
  letter-spacing:.01em;
  color:var(--milk);
  display:flex; align-items:center; gap:.42em;
}
.mark__star{
  width:clamp(15px,2.4vw,19px);
  height:auto;
  flex:none;
}

/* Spotify tints its embed from the playlist art, so it can land any colour.
   A violet bezel makes that read as an inset screen rather than a foreign
   block dropped into the card. */
.player{
  position:relative;
  margin-top:11px;
  border-radius:20px;
  padding:8px;
  background:rgb(21 15 34 / .55);
  box-shadow:inset 0 0 0 1px rgb(107 74 168 / .5);
  min-height:80px;
}
.player iframe{ display:block; border:0; width:100%; border-radius:13px; }

.player__hint{
  margin:7px 0 1px;
  text-align:center;
  font-family:var(--f-hand);
  font-size:1.04rem;
  line-height:1.1;
  color:var(--lilac);
  transition:opacity .45s ease;
}
.player.is-playing .player__hint,
.player.is-dead .player__hint{ opacity:0; pointer-events:none; }

/* no playlist id yet: say so, rather than showing an empty box */
.player.is-idle{
  display:grid; place-items:center;
  min-height:92px;
  background:linear-gradient(150deg, rgb(107 74 168 / .5), rgb(74 157 122 / .38));
  color:var(--milk);
  font-family:var(--f-hand);
  font-size:1.12rem;
}
.player.is-idle::after{ content:"♪  add a spotify link"; }
.player.is-idle .player__hint{ display:none; }

/* ─────────────────── the moon phases: the divider ───────────────────
   eight phases in a row. each is a gold disc with a second disc of the
   card colour slid across it, so the terminator curves the way a real
   one does. --o moves that disc: 0% new, 100% full. */

.moonline{
  padding:clamp(13px,2.2vw,17px) 2px clamp(9px,1.6vw,12px);
  border-top:1px solid rgb(107 74 168 / .45);
  border-bottom:1px solid rgb(107 74 168 / .45);
}

.phases{
  display:flex; align-items:center; justify-content:space-between;
  gap:4px;
}

/* names the lit disc, so the row reads as a reading of tonight rather than
   as eight decorative dots */
.moonline__now{
  margin:clamp(8px,1.4vw,11px) 0 0;
  text-align:center;
  font-family:var(--f-hand);
  font-size:1.02rem;
  line-height:1;
  color:var(--lilac);
}

.ph{
  position:relative;
  width:clamp(13px,2.6vw,16px);
  aspect-ratio:1;
  border-radius:50%;
  background:var(--brass);
  overflow:hidden;
  opacity:.5;
  transition:opacity .3s ease;
}
.ph::after{
  content:"";
  position:absolute; inset:0;
  border-radius:50%;
  background:var(--sky);
  transform:translateX(calc(var(--o) * -1));
}
/* waning half of the cycle: the shadow comes from the other side */
.ph--w::after{ transform:translateX(var(--o)); }

/* tonight's phase is the lit one */
.phases[data-phase="0"] .ph:nth-child(1),
.phases[data-phase="1"] .ph:nth-child(2),
.phases[data-phase="2"] .ph:nth-child(3),
.phases[data-phase="3"] .ph:nth-child(4),
.phases[data-phase="4"] .ph:nth-child(5),
.phases[data-phase="5"] .ph:nth-child(6),
.phases[data-phase="6"] .ph:nth-child(7),
.phases[data-phase="7"] .ph:nth-child(8){
  opacity:1;
  background:var(--moon);
  box-shadow:0 0 0 3px rgb(240 215 140 / .16), 0 0 14px rgb(240 215 140 / .45);
}

/* ───────────────────────── tabs ───────────────────────── */

.tabs{
  display:flex; gap:7px; flex-wrap:wrap;
  padding:clamp(13px,2vw,16px) 0 0;
}
.tab{
  font-family:var(--f-display);
  font-weight:600;
  font-size:.9rem;
  color:var(--lilac);
  background:rgb(107 74 168 / .22);
  border:1px solid rgb(107 74 168 / .5);
  border-radius:999px;
  padding:7px 17px;
  cursor:pointer;
  transition:background .18s ease, color .18s ease, transform .14s ease, border-color .18s ease;
}
.tab:hover{ background:rgb(107 74 168 / .42); color:var(--milk); transform:translateY(-1px); }
.tab.is-on{
  background:var(--moon);
  border-color:var(--moon);
  color:var(--ink);
  box-shadow:0 5px 16px -6px rgb(240 215 140 / .7);
}

/* ───────────────────────── body ───────────────────────── */

.body{
  display:grid;
  grid-template-columns:minmax(0,148px) minmax(0,1fr);
  gap:clamp(14px,3vw,26px);
  padding-top:clamp(16px,2.6vw,22px);
}

.rail{ text-align:center; }

/* her photo, ringed in gold. if it is missing the ring becomes a crescent
   so the rail never collapses into a gap. */
.pfp{
  width:clamp(92px,15vw,112px);
  aspect-ratio:1;
  margin:0 auto 14px;
  border-radius:50%;
  padding:3px;
  background:conic-gradient(from 210deg, var(--moon), var(--brass) 45%, var(--fern) 72%, var(--moon));
  box-shadow:0 10px 26px -10px rgb(0 0 0 / .8);
}
.pfp img{
  width:100%; height:100%;
  border-radius:50%;
  object-fit:cover;
  background:var(--dusk);
}
.pfp.is-empty{ display:grid; place-items:center; }
.pfp.is-empty::after{
  content:"☾";
  font-size:2.4rem; line-height:1;
  color:var(--moon);
}

.rail__title{
  margin:0;
  font-family:var(--f-display);
  font-weight:700;
  font-size:clamp(1.3rem,3.4vw,1.58rem);
  line-height:1.1;
  color:var(--milk);
  text-transform:lowercase;
}
.rail__title::after{
  content:"";
  display:block;
  width:40px; height:3px;
  margin:9px auto 0;
  border-radius:99px;
  background:linear-gradient(90deg, var(--fern), var(--moon));
}
.rail__sub{
  margin:.6rem 0 0;
  font-size:.8rem; font-weight:600;
  color:var(--lilac);
  line-height:1.42;
}

.socials{
  display:flex; justify-content:center; flex-wrap:wrap; gap:9px;
  margin-top:13px;
}
.socials:empty{ display:none; }
/* one rule for both kinds of chip: a link that opens, and a button that copies
   a handle there is no url for */
.chip{
  position:relative;
  width:32px; height:32px;
  display:grid; place-items:center;
  border:0; padding:0;
  border-radius:50%;
  background:rgb(107 74 168 / .3);
  color:var(--lilac);
  cursor:pointer;
  font:inherit;
  transition:background .18s ease, color .18s ease, transform .14s ease;
}
.chip:hover{ background:var(--moon); color:var(--ink); transform:translateY(-2px); }
.chip svg{
  width:17px; height:17px;
  fill:none; stroke:currentColor;
  stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round;
}

/* copied: the chip says so itself, rather than firing a toast from somewhere
   else on the page */
.chip.is-copied{ background:var(--mint); color:var(--ink); }
.chip.is-copied::after{
  content:"copied";
  position:absolute; top:calc(100% + 6px); left:50%;
  transform:translateX(-50%);
  padding:3px 8px;
  border-radius:8px;
  background:var(--mint); color:var(--ink);
  font-family:var(--f-body); font-weight:700; font-size:.66rem;
  letter-spacing:.02em;
  white-space:nowrap;
  pointer-events:none;
}

/* ───────────────────────── rows ───────────────────────── */

.panel[hidden]{ display:none; }
.rows{ margin:0; }

.row{
  padding:11px 0;
  border-bottom:1px solid rgb(107 74 168 / .38);
}
.row:first-child{ padding-top:2px; }
.row:last-child{ border-bottom:0; padding-bottom:2px; }

.row dt{
  display:inline;
  font-family:var(--f-display);
  font-weight:600;
  font-size:.96rem;
  color:var(--moon);
  margin-right:.44em;
}
.row dd{
  display:inline;
  margin:0;
  font-size:.94rem; font-weight:500;
  color:var(--milk);
}
.row dd a{
  color:var(--mint);
  text-decoration-color:rgb(127 211 174 / .45);
  text-underline-offset:3px;
  transition:color .16s ease;
}
.row dd a:hover{ color:var(--moon); }

.note{
  margin:16px 0 0;
  text-align:center;
  font-family:var(--f-hand);
  font-size:1.14rem;
  color:var(--lilac);
}

/* ───────────────────────── the shelf ───────────────────────── */

.shelf{
  display:flex; align-items:flex-end; justify-content:center;
  flex-wrap:wrap;
  gap:clamp(8px,1.8vw,16px);
  margin-top:clamp(18px,3vw,26px);
  padding-top:clamp(14px,2.4vw,20px);
  border-top:1px solid rgb(107 74 168 / .45);
}
/* nothing dropped in images/ yet: the rule would float above empty space */
.shelf:empty{ display:none; }

/* these objects are photographs at wildly different proportions — a dragonfly
   is wide and flat, a potion bottle is tall. bounding both axes keeps them
   looking like one set on one shelf instead of a size contest. */
.shelf img{
  width:auto; height:auto;
  max-height:clamp(40px,7.6vw,58px);
  max-width:clamp(48px,9vw,70px);
  transition:transform .22s ease;
}
.shelf img:hover{ transform:translateY(-5px) rotate(-4deg); }

/* ─────────────── the note, and the seal that closes it ───────────────
   cut-paper sign-off at the bottom left. sits slightly askew because it was
   torn out and put there, not typeset. */

.tuck{
  margin-top:clamp(16px,2.6vw,22px);
  padding-right:clamp(78px,13vw,112px);   /* clears the wax seal */
}
.tuck__note{
  width:clamp(150px,32vw,224px);
  height:auto;
  transform:rotate(-2.5deg);
  opacity:.94;
  filter:drop-shadow(0 6px 12px rgb(0 0 0 / .45));
}
.tuck__seal{ display:none; }   /* one seal on the card is enough */

/* the cat sits on her moon and closes the card */
.seal{
  position:absolute;
  right:clamp(-14px,-1.8vw,-8px); bottom:clamp(-20px,-2.6vw,-12px);
  width:clamp(66px,11vw,94px);
  height:auto;
  z-index:3;
  pointer-events:none;
  transform:rotate(-7deg);
  filter:drop-shadow(0 10px 18px rgb(0 0 0 / .6));
}

/* ───────────────────────── small screens ───────────────────────── */

/* the sky objects are atmosphere for a wide screen. on a phone the card fills
   the viewport, so they would sit behind it and never be seen — the crescent
   anchored to the card takes over the job. */
@media (max-width:740px){
  .sky__moon, .sky__face, .sky__star{ display:none; }

  .stage__moon{
    display:block;
    position:absolute;
    top:clamp(-104px,-24vw,-72px); left:clamp(-74px,-17vw,-52px);
    width:clamp(168px,44vw,224px);
    height:auto;
    z-index:-1;
    opacity:.44;
    filter:saturate(2.4) brightness(1.04);
    pointer-events:none;
  }
  /* room above the card for the moon to clear it */
  body{ padding-top:clamp(78px,19vw,112px); }

  /* thumbs, not cursors */
  .tab{ padding:9px 18px; }
  .chip{ width:38px; height:38px; }
  .chip svg{ width:19px; height:19px; }
}

@media (max-width:560px){
  .body{ grid-template-columns:1fr; padding-top:clamp(14px,2.2vw,18px); }

  /* stacked, her photo and name cost 217px of a 946px card — more than the
     content sitting beside them. side by side it costs about 95px, and reads
     like a profile header instead of a column with one chip at the bottom. */
  .rail{
    display:grid;
    grid-template-columns:auto minmax(0,1fr);
    grid-template-areas:
      "pic title"
      "pic sub"
      "pic soc";
    column-gap:15px;
    text-align:left;
    padding-bottom:15px;
    border-bottom:1px solid rgb(107 74 168 / .38);
  }
  .pfp{ grid-area:pic; width:78px; margin:0; align-self:center; }
  .rail__title{ grid-area:title; align-self:end; font-size:1.4rem; }
  .rail__title::after{ margin:7px 0 0; }          /* underline follows the text left */
  .rail__sub{ grid-area:sub; margin:3px 0 0; }
  .socials{ grid-area:soc; justify-content:flex-start; margin-top:8px; }

  .phases{ gap:2px; }

  /* centred on a phone, so the padding that held it off the right goes. sized
     a little smaller than it is on desktop: centred at 320px the full-width
     note comes within 13px of the wax seal, which reads as a near-miss rather
     than a margin. */
  .tuck{
    display:flex;
    justify-content:center;
    padding-right:0;
  }
  .tuck__note{ width:clamp(140px,40vw,196px); }

  /* eight objects wrap to 6 + 2 on a phone, and a row of two reads as a
     mistake. a four-column grid makes it 4 + 4 on purpose, and the equal
     cells let each one sit a little larger than it could in a flex row. */
  .shelf{
    display:grid;
    grid-template-columns:repeat(4,1fr);
    justify-items:center; align-items:end;
    row-gap:16px;
  }
  .shelf img{ max-height:52px; max-width:64px; }
}

/* reduced motion takes away the movement, not the picture — the moon and the
   star are static, so they stay. only the drifting stops. */
@media (prefers-reduced-motion:reduce){
  body{ animation:none; }
  .mote{ display:none; }
  .tab:hover, .chip:hover, .shelf img:hover{ transform:none; }
}
