/* ============================================================
   cards.css — the three content blocks the page repeats
   ============================================================
     .shot      a screenshot of the app, at the ratio it was
                captured in. Used everywhere one goes, the
                hero's included.
     .rail      a horizontal row of .course cards, scrolled
     .features  a grid of .feature blocks (a .shot plus a caption)

   Every one is aspect-ratio driven, so a block keeps its shape
   in a full-width slot, a two-up, or a three-up without a single
   height being restated. Type comes from the heading scale:
   captions are h3/h4, meta is .muted.
   ============================================================ */

/* ---------- Screenshot slot --------------------------------------- */

/* Nearly every capture is a white app screen, and a white screen on a white
   page has no edge at all.

   An outline is the wrong instrument for it. A line says "here is a boundary"
   without saying the two sides are different things, so a white shot in a thin
   box still reads as page — it just gains a rectangle. The app never outlines
   a white surface either; it tells surfaces apart by FILLING them.

   So the slot is a mat: a surround one step darker than any page it sits on,
   with the capture inset in it. What separates image from page is a change of
   ground, which needs no line and no paint-order trick. `muted` clears both
   white (#FFF) and the accent band (#F7F7F7), so no section is special-cased.

   Radii stay concentric: the image keeps lg, the same corner the course cards
   take, and the mat's own is that plus whatever it is inset by — so the two
   arcs stay parallel however thin --mat is set, instead of the outer one
   crowding the inner. One value controls the whole thing.

   The ratio lives on the image, not the mat: the mat's box includes its
   padding, so putting it there would skew every --shot by the inset. */
.shot          { --mat: var(--space-2);
                 background: var(--color-muted); padding: var(--mat);
                 border-radius: calc(var(--radius-lg) + var(--mat)); }
.shot > *,
.shot img      { display: block; width: 100%; height: auto;
                 aspect-ratio: var(--shot, 15 / 8); object-fit: cover;
                 border-radius: var(--radius-lg); }

#practice      { --shot: 1104 / 675; }

/* ---------- Course rail ------------------------------------------- */

/* A carousel that also runs itself. The track holds the list twice and the
   rail's own scrollLeft wraps at the halfway mark, so the loop is seamless
   whether the marquee is advancing it or the reader is dragging it — one
   position, one mechanism, not an animation fighting a scroller (see the
   rail script in index.html).

   Spacing rides on each card's margin-right rather than a flex gap, so the
   halfway point lands exactly on a card boundary — a gap would leave the seam
   half a gutter off. scroll-behavior is pinned to auto because the document
   sets smooth, which would turn every frame's nudge into an animation. */
.rail          { overflow-x: auto; overflow-y: hidden; scroll-behavior: auto;
                 scrollbar-width: none; overscroll-behavior-x: contain;
                 cursor: grab; }
.rail:active   { cursor: grabbing; }
.rail::-webkit-scrollbar { display: none; }
.rail-track    { display: flex; width: max-content; }

/* ---------- Course card ------------------------------------------- */

/* A translation of the app's CoverHero (library/lib/src/components/
   cover_hero.dart) at card size — the same card the library's Featured strip
   shows. The cover's bottom melts into a band of the cover's OWN sampled
   bottom colour, which then carries the name and description: the Apple-Music
   "the header is the artwork's colour" look.

   --tint (the sampled colour) and --fg (whichever of the two base themes
   contrasts with it) are set per card by the page's sampler, which mirrors
   _coverTintProvider. Until they land the card holds the accent surface and
   stays invisible, then reveals whole — image, band and header in one 200ms
   fade, exactly as CoverHero does. It never slides. */

/* One size in every place a card appears — the rail, the create demo and the
   situation picker all read --card, and so does the grid track beside it, so
   the card and the column holding it can never disagree. It steps down once,
   on mobile. max-width is a floor for phones narrower than the card plus its
   gutters, not a third size. */
:root          { --card: 320px; }
@media (max-width: 768px) { :root { --card: 280px; } }

.course        { --tint: 247 247 247;              /* accent, until sampled */
                 --fg: 34 34 34;
                 width: var(--card); max-width: 100%;
                 display: block; overflow: hidden;
                 border-radius: var(--radius-lg);  /* Radii.lg, all corners */
                 background: rgb(var(--tint)); color: rgb(var(--fg));
                 opacity: 0; transition: opacity 200ms ease;
                 /* A card is a drag handle as much as a link, so nothing in it
                    may start a native text or image drag. */
                 user-select: none; -webkit-user-select: none; }
.course.on     { opacity: 1; }
/* Where it sits in the rail is the rail's business, not the card's — the
   create section below shows the same card outside any of this. */
.rail .course  { flex: none; margin-right: var(--space-4); }

/* _kHeroRatio — also the covers' own 1600x1200, so nothing is cropped. */
.course-cover     { position: relative; display: block; aspect-ratio: 4 / 3; }
.course-cover img { display: block; width: 100%; height: 100%; object-fit: cover;
                    -webkit-user-drag: none; }

/* The melt: the cover's bottom quarter easing into the band on a smoothstep
   ramp — t²(3−2t) sampled at equal stops, so neither its top edge on the photo
   nor its foot on the flat band reads as a line. It overhangs the seam by a
   pixel, which paints over the cover's antialiased last row. */
.course-melt   { position: absolute; left: 0; right: 0; bottom: -1px;
                 height: calc(25% + 1px); pointer-events: none;
                 background: linear-gradient(to bottom,
                   rgb(var(--tint) / 0)     0%,
                   rgb(var(--tint) / .104) 20%,
                   rgb(var(--tint) / .352) 40%,
                   rgb(var(--tint) / .648) 60%,
                   rgb(var(--tint) / .896) 80%,
                   rgb(var(--tint) / 1)   100%); }

/* No top padding: the melt has already reached the flat band at the seam, so
   the title starts there (CoverHero's own fromLTRB(pad, 0, pad, pad)). */
.course-head     { display: block; padding: 0 var(--space-6) var(--space-6); }
.course-head b   { display: block; font-size: 24px;                     /* H2 */
                   font-weight: var(--font-weight-medium); letter-spacing: -.02em;
                   white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.course-head span { display: block; margin-top: var(--space-2);
                    font-size: var(--font-size-base);                   /* Subtitle */
                    /* ColorScheme.mutedForeground on a tinted scheme:
                       the foreground at 55% over the tint. */
                    color: color-mix(in srgb, rgb(var(--fg)) 55%, rgb(var(--tint))); }

/* ---------- Create -------------------------------------------------- */

/* The app's create field, typing itself, over the skill that line produces.
   A mock rather than a form: no input, no button, nothing focusable — it
   shows what the surface does, and the section's button is what goes there.

   The card underneath is a plain .course, built by the same renderer and the
   same tint cache as the rail's, so this is the object you would really get
   rather than a picture of one. */
/* Wide enough for the skill the line produces; the bar keeps its own, narrower
   measure below, because a field the width of a two-column block reads as a
   page rather than as something you type one line into. */
.make          { width: 100%; max-width: calc(860px + 2 * var(--space-6));
                 padding: 0 var(--space-6);
                 display: flex; flex-direction: column; align-items: center;
                 gap: var(--space-12); }

/* Padded to the button on the right and to the text on the left, the way the
   app's own field is: the send button's own box supplies the right inset. */
.make-bar      { width: 100%; max-width: 640px; display: flex; align-items: center;
                 padding: var(--space-2) var(--space-2) var(--space-2) var(--space-6);
                 border-radius: 999px;
                 background: var(--color-input);
                 border: 1px solid var(--color-border);
                 box-shadow: var(--shadow-elevated);
                 font-size: var(--font-size-base); text-align: left; }

/* Sits where the next character will land, so it must hug the text. */
.make-caret    { width: 2px; height: 1.15em; flex: none; margin-left: 1px;
                 background: var(--color-primary);
                 animation: caret 1.05s steps(1) infinite; }
@keyframes caret { 50% { opacity: 0; } }

.make-send     { margin-left: auto; flex: none;
                 width: 36px; height: 36px; border-radius: 999px;
                 display: flex; align-items: center; justify-content: center;
                 background: var(--color-muted); color: var(--color-foreground); }
.make-send svg { width: 16px; height: 16px; }

/* The result appears the moment the line is finished, and changes the same
   way the situation picker does — no fade either time. The card's own reveal
   is neutralised inside .use (see above), so there is nothing left to animate
   and the slot needs no state of its own. */
.make-out      { width: 100%; }

/* The caret is the only thing left moving in here. */
@media (prefers-reduced-motion: reduce) {
  .make-caret  { animation: none; }
}

/* ---------- Situation picker --------------------------------------- */

/* The skill for where you actually are: a row of situations over the card it
   lands on, beside what you practice in it. The card is a plain .course and
   the row is the same .tabs component the library uses, so the only thing
   this adds is the two-column pairing. */
/* The cover and what it covers, side by side and the same height. */
.use .course   { opacity: 1; }
/* Two lines' worth whatever the copy runs to, so the row's height is the same
   for every skill and nothing below it shifts as the demo cycles. */
.use .course-head span { min-height: 3em; }

/* width:100% is load-bearing. In the situations section this is a plain block
   and would fill on its own, but in the create demo it is a GRID ITEM, and an
   auto margin beats `stretch` — the block would size to its content and the
   modules track would collapse to nothing. Stating the width leaves the auto
   margins no free space to take, so it fills in both hosts and still centres
   once max-width bites. */
.use           { width: 100%; max-width: 860px; margin: 0 auto;
                 display: grid; grid-template-columns: var(--card) minmax(0, 1fr);
                 gap: var(--space-12); }

/* The modules scroll within the card's own height, so the two columns stay
   level however many topics a skill has. That height has to come from the
   CARD alone: a scroller that sizes to its content would set the row's height
   itself and then have nothing left to scroll. Taking it out of flow is what
   leaves the card as the only thing measuring the row.

   scroll-behavior is pinned to auto because the document sets smooth, which
   would animate the jump back to the top on every switch. */
.use-col       { position: relative; }
/* No overscroll containment: run out of modules, either end, and the page
   picks the scroll up from there. Trapping it would leave a reader stuck on a
   block a third of the screen tall. */
.use-scroll    { position: absolute; inset: 0; overflow-y: auto;
                 scroll-behavior: auto; scrollbar-width: none; }
.use-scroll::-webkit-scrollbar { display: none; }

/* On a phone the cover stands alone, centred. Side by side there is no width
   left for a module name, and putting the modules underneath buries the card
   under a scroller taller than itself — so they go, and the card keeps its
   own size rather than shrinking to make room for something not there. */
@media (max-width: 900px) {
  .use         { grid-template-columns: minmax(0, 1fr); justify-items: center; }
  .use-col     { display: none; }
}


/* ---------- Feature grid ------------------------------------------ */

/* One grid rule covers the two-up and the three-up. */
.features      { display: grid; grid-template-columns: repeat(2, 1fr);
                 gap: var(--space-6); }
.features--3   { grid-template-columns: repeat(3, 1fr); }
.feature       { display: flex; flex-direction: column; gap: var(--space-6); }

/* The caption reads as one block hanging off the card's left edge: a wide gap
   above it separates it from the card, a hairline inside it binds the title to
   its description, and it stops short of the card so the line breaks early. */
.feature-t     { display: flex; flex-direction: column; gap: var(--space-0);
                 align-self: flex-start; width: 80%; text-align: left; }
.feature-t b   { font-size: 18px; font-weight: var(--font-weight-medium); }
/* Two lines' worth of space whatever the copy runs to, so every caption in a
   row is the same height and the blocks sit on one baseline. The copy is
   written to fit two lines; this only stops a short one collapsing to one. */
.feature-t p   { min-height: 3em; color: var(--color-muted-foreground); }

/* Three narrow cards read as a set rather than a list, so their captions
   centre under the card instead of hanging off its left edge. */
.features--3 .feature-t { align-self: center; text-align: center; }

/* Stacked, the cards need the gap of a section break between them, not the
   gap of a column. */
@media (max-width: 900px) {
  .features,
  .features--3 { grid-template-columns: 1fr; gap: var(--space-16); }
}

@media (prefers-reduced-motion: reduce) {
  .course      { animation: none; }
}
